以不同方式使用scrapy处理分页站点

时间:2018-04-20 15:00:45

标签: python python-3.x web-scraping pagination scrapy

我使用scrapy在python中编写了一个脚本来解析网页中的一些信息。该网页中可用的数据遍历分页。如果我使用response.follow(),那么我可以完成它。不过,我想按照requestsBeautifulSoup scrapyrequests实施的逻辑,但无法找到任何想法。

使用BeautifulSoupimport requests from bs4 import BeautifulSoup page = 0 URL = 'http://esencjablog.pl/page/{}/' while True: page+=1 res = requests.get(URL.format(page)) soup = BeautifulSoup(res.text,'lxml') items = soup.select('.post_more a.qbutton') if len(items)<=1:break for a in items: print(a.get("href")) 我可以提出这样做​​,这很好:

scrapy

我想按照我上面应用的逻辑使用class PaginationTestSpider(scrapy.Spider): name = 'pagination' start_urls = ['http://esencjablog.pl/page/{}/'.format(page) for page in range(1,63)] #I used 63 here because the highest page number is 62 def parse(self, response): for link in response.css('.post_more a.qbutton'): yield{"link":link.css('::attr(href)').extract_first()} 做同样的事情,但每次我尝试执行它时,我最终会做如下的事情:

scrapy

再一次:我的问题是,如果我希望在requests中使用BeautifulSoup和{{1}}时最后页码未知的话,那么结构如何?是

3 个答案:

答案 0 :(得分:3)

在这种情况下,您无法利用并行下载,但由于您希望在Scrapy中模拟相同的内容,因此可以通过不同的方式实现

方法1 - 使用页码的收益页面

class PaginationTestSpider(scrapy.Spider):
    name = 'pagination'

    # Start with page #1
    start_urls = ['http://esencjablog.pl/page/1/']


    def parse(self, response):
        # we commnicate the page numbers using request meta
        # this is not mandatory as we can extract the same data from 
        # the response.url also. But I prefer using meta here

        page_no = response.meta.get('page', 1) + 1

        items = response.css('.post_more a.qbutton')
        for link in items:
            yield{"link":link.css('::attr(href)').extract_first()}

        if items:
            # if items were found we move to the next page
            yield Request("http://esencjablog.pl/page/{}".format(page_no), meta={"page": page_no}, callback=self.parse)

理想的方法通常是,如果您可以从第一个请求中找到最后一页的计数,那么您将提取该号码并在第一次parse调用中将所有请求解压为一个。但这只有在可以知道最后页码

的情况下才有效

方法2 - 使用对象产生下一页

class PaginationTestSpider(scrapy.Spider):
    name = 'pagination'

    # Start with page #1
    start_urls = ['http://esencjablog.pl/page/1/']


    def parse(self, response):
        items = response.css('.post_more a.qbutton')
        for link in items:
            yield{"link":link.css('::attr(href)').extract_first()}

        next_page = response.xpath('//li[contains(@class, "next_last")]/a/@href')
        if next_page:
            yield response.follow(next_page) # follow to next page, and parse again

这只不过是@Konstantin所提到的一个直截了当的副本。对不起,但想让这个更完整的答案

方法3 - 在第一次回复时产生所有页面

class PaginationTestSpider(scrapy.Spider):
    name = 'pagination'

    # Start with page #1
    start_urls = ['http://esencjablog.pl/page/1/']
    first_request =  True

    def parse(self, response):
        if self.first_request:
            self.first_request = False
            last_page_num = response.css("fa-angle-double-right::href").re_first("(\d+)/?$")

            # yield all the pages on first request so we take advantage to parallel downloads
            for page_no in range(2, last_page_num + 1):
                yield Request("http://esencjablog.pl/page/{}".format(page_no), callback=self.parse)

        items = response.css('.post_more a.qbutton')
        for link in items:
            yield {"link":link.css('::attr(href)').extract_first()}

此方法的最佳之处在于您浏览第一页,然后检查最后一页计数,并生成所有页面,以便同时进行下载。前两种方法本质上更顺序,如果你不想加载网站,你只会遵循它们。刮刀的理想方法是Approach 3

现在关于meta对象的使用,在下面的链接

中有很好的解释

https://doc.scrapy.org/en/latest/topics/request-response.html#passing-additional-data-to-callback-functions

在此处添加相同内容以供参考

将其他数据传递给回调函数

请求的回调是在下载该请求的响应时将被调用的函数。将使用下载的Response对象作为其第一个参数调用回调函数。

示例:

def parse_page1(self, response):
    return scrapy.Request("http://www.example.com/some_page.html",
                          callback=self.parse_page2)

def parse_page2(self, response):
    # this would log http://www.example.com/some_page.html
    self.logger.info("Visited %s", response.url)

在某些情况下,您可能有兴趣将参数传递给那些回调函数,以便稍后在第二个回调中接收参数。您可以使用Request.meta属性。

以下是如何使用此机制传递项目以填充不同页面中的不同字段的示例:

def parse_page1(self, response):
    item = MyItem()
    item['main_url'] = response.url
    request = scrapy.Request("http://www.example.com/some_page.html",
                             callback=self.parse_page2)
    request.meta['item'] = item
    yield request

def parse_page2(self, response):
    item = response.meta['item']
    item['other_url'] = response.url
    yield item

答案 1 :(得分:0)

你必须使用scrapy.Request:

find /path/to/files -type f -mtime +30 -printf '%Ta %p\n' \
    | grep -v ^Mon | cut -c5- | tr "\n" "\0" | xargs -0 rm -v

您可以在the scrapy documentation

中找到更多详情

答案 2 :(得分:0)

您可以遍历这些页面scrapy doc

class PaginationTestSpider(scrapy.Spider):
    name = 'pagination'
    start_urls = ['http://esencjablog.pl/page/1/'] # go to first page

    def parse(self, response):
        for link in response.css('.post_more a.qbutton'):
            yield{"link":link.css('::attr(href)').extract_first()}

        next_page = response.xpath('//li[contains(@class, "next_last")]/a/@href')
        if next_page:
            yield response.follow(next_page) # follow to next page, and parse again