-
要获取尺寸,请从 xpath 中删除“tbody”,并在“key”td 之后搜索下一个兄弟姐妹(以获取“value”td 文本)。
-
设置'process_value'以正确获取页码。
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class ToscrapeSpider(CrawlSpider):
name = 'toscrape'
allowed_domains = ['pstrial-2019-12-16.toscrape.com']
start_urls = ['http://pstrial-2019-12-16.toscrape.com/browse/insunsh']
rule_articles_page = Rule(LinkExtractor(restrict_xpaths="//div[@id='body']/div[2]/a"), callback='parse_item', follow=False)
rule_next_page = Rule(LinkExtractor(restrict_xpaths="//form[@class='nav next']/input[1]", tags=('input'), attrs=('value',),
process_value=(lambda x: f'?page={x[-1]}')),
follow=True,)
rules = (
rule_articles_page,
rule_next_page,
)
def parse_item(self, response):
yield {
'Image': response.xpath("//div[@id='body']/img/@src").get(),
'Title': response.xpath("//div[@id='content']/h1/text()").get(),
'artist': response.xpath("//div[@id='content']/h2/text()").get(),
'Description': response.xpath("//div[@class='description']/p/text()").get(),
'URL': response.url,
'Dimension': response.xpath('//tr/td[text()="Dimensions"]/following-sibling::td[@class="value"]/text()').get(),
}
输出:
[scrapy.core.scraper] DEBUG: Scraped from <200 http://pstrial-2019-12-16.toscrape.com/item/12125/Front_Panel_for_Blouse?back=155>
{'Image': '/content/12125.jpg', 'Title': 'Front Panel for Blouse', 'artist': None, 'Description': 'reverse patchwork of animal designs in blue, yellow, red and orange', 'URL': 'http://pstrial-2019-12-16.toscrape.com/item/12125/Front_Panel_for_Blouse?back=155', 'Dimension': '16 3/4 x 22 1/8in. (42.5 x 56.2cm)'}
...
...
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=2> (referer: http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=1)
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://pstrial-2019-12-16.toscrape.com/item/12712/The_Muses_of_Music_and_Poetry?back=155> (referer: http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=1)
[scrapy.core.scraper] DEBUG: Scraped from <200 http://pstrial-2019-12-16.toscrape.com/item/12712/The_Muses_of_Music_and_Poetry?back=155>
{'Image': '/content/12712.jpg', 'Title': 'The Muses of Music and Poetry', 'artist': 'Sculptor: Guillaume Coustou the Younger', 'Description': None, 'URL': 'http://pstrial-2019-12-16.toscrape.com/item/12712/The_Muses_of_Music_and_Poetry?back=155', 'Dimension': '24 5/8 x 15 1/2 x 10 1/2 in. (62.55 x 39.37 x 26.67 cm)'}
...
...
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=3> (referer: http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=2)
[scrapy.core.engine] DEBUG: Crawled (200) <GET http://pstrial-2019-12-16.toscrape.com/item/13484/Don_Pascual_y_su_esposa?back=155> (referer: http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=2)
...
...
...
编辑:
上面的代码只能获取最多 9 个页面,包括。这是修复:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
def get_next_page(value):
next_page = value.replace('browse/', 'browse/insunsh?page=')
return next_page
class ToscrapeSpider(CrawlSpider):
name = 'toscrape'
allowed_domains = ['pstrial-2019-12-16.toscrape.com']
start_urls = ['http://pstrial-2019-12-16.toscrape.com/browse/insunsh']
rule_articles_page = Rule(LinkExtractor(restrict_xpaths="//div[@id='body']/div[2]/a"), callback='parse_item', follow=False)
rule_next_page = Rule(LinkExtractor(restrict_xpaths="//form[@class='nav next']/input[1]", tags=('input'), attrs=('value',),
process_value=get_next_page),
follow=True)
rules = (
rule_articles_page,
rule_next_page,
)
def parse_item(self, response):
yield {
'Image': response.xpath("//div[@id='body']/img/@src").get(),
'Title': response.xpath("//div[@id='content']/h1/text()").get(),
'artist': response.xpath("//div[@id='content']/h2/text()").get(),
'Description': response.xpath("//div[@class='description']/p/text()").get(),
'URL': response.url,
'Dimension': response.xpath('//tr/td[text()="Dimensions"]/following-sibling::td[@class="value"]/text()').get(),
}
防止抓取空白页面:
使用 CrawlSpider 的解决方案:
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy.exceptions import CloseSpider
def get_next_page(value):
next_page = value.replace('browse/', 'browse/insunsh?page=')
return next_page
class ToscrapeSpider(CrawlSpider):
name = 'toscrape'
allowed_domains = ['pstrial-2019-12-16.toscrape.com']
start_urls = ['http://pstrial-2019-12-16.toscrape.com/browse/insunsh']
rule_articles_page = Rule(LinkExtractor(restrict_xpaths="//div[@id='body']/div[2]/a", deny=('/tarpit/die-scrapers-die')), callback='parse_item', follow=False)
rule_next_page = Rule(LinkExtractor(restrict_xpaths="//form[@class='nav next']/input[1]", tags=('input'), attrs=('value',),
process_value=get_next_page),
follow=True, callback='check_empty_page')
rules = (
rule_articles_page,
rule_next_page,
)
def parse_item(self, response):
yield {
'Image': response.xpath("//div[@id='body']/img/@src").get(),
'Title': response.xpath("//div[@id='content']/h1/text()").get(),
'artist': response.xpath("//div[@id='content']/h2/text()").get(),
'Description': response.xpath("//div[@class='description']/p/text()").get(),
'URL': response.url,
'Dimension': response.xpath('//tr/td[text()="Dimensions"]/following-sibling::td[@class="value"]/text()').get(),
}
def check_empty_page(self, response):
# if the number of urls is less than or equal to 1 then close the spider
# (The first url is always '/tarpit/die-scrapers-die' so there's always at least one url)
if len(response.xpath("//div[@id='body']/div[2]/a")) <= 1:
raise CloseSpider("Finished")
get_next_page 函数说明:Scrapy 返回 'http://pstrial-2019-12-16.toscrape.com/browse/' 的基本 url 并添加页面值,例如第一页的值将是“http://pstrial-2019-12-16.toscrape.com/browse/1”。所以我们将其替换为 'http://pstrial-2019-12-16.toscrape.com/browse/insunsh?page=1' 并返回值。
用scrapy.Spider解决:
import scrapy
class ToscrapeSpider(scrapy.Spider):
name = 'toscrape'
allowed_domains = ['pstrial-2019-12-16.toscrape.com']
start_urls = ['http://pstrial-2019-12-16.toscrape.com/browse/insunsh']
def parse(self, response, **kwargs):
# (The first url is always '/tarpit/die-scrapers-die' so there's always at least one url)
if len(response.xpath("//div[@id='body']/div[2]/a")) > 1:
for url in response.xpath('//div[@id="body"]/div[2]/a/@href').getall():
yield response.follow(url=url, callback=self.parse_item)
next_page = response.xpath('//form[@class="nav next"]/input[1]/@value').get()
next_page = f'{self.start_urls[0]}?page={next_page}'
yield scrapy.Request(url=next_page, callback=self.parse)
def parse_item(self, response):
yield {
'Image': response.xpath("//div[@id='body']/img/@src").get(),
'Title': response.xpath("//div[@id='content']/h1/text()").get(),
'artist': response.xpath("//div[@id='content']/h2/text()").get(),
'Description': response.xpath("//div[@class='description']/p/text()").get(),
'URL': response.url,
'Dimension': response.xpath('//tr/td[text()="Dimensions"]/following-sibling::td[@class="value"]/text()').get(),
}