Scrapy递归爬网并从每个页面获取数据

时间:2017-09-08 23:40:49

标签: python recursion scrapy

一系列json页面通过该页面上的最后一个id相互链接。该网页的网址为example.com/20/id 所以我想抓取第一页,保存数据,获取该页面的最后一个ID并抓取另一页:example.com/40/new_id依此类推,每次都会有20个结果。我不知道有多少页面,所以当没有id时我会停下来。

这应该是一个简单的递归,但我不知道如何做,并在同一时间保存数据。我对此感到困惑:

yield scrapy.Request(url, self.parse) 

在我看来应该递归,直到没有id,但它只运行2次。

import scrapy
import json
import logging
from w3lib.url import add_or_replace_parameter
import re
class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = ['https://www.example/0/1234']

    def parse(self, response): # check if there is an id
        data = json.loads(str(response.body, 'utf-8'))
        dataLength = len(data)
        page = 0
        if data[dataLength - 1]["id"]:
            page += 20
            url = add_or_replace_parameter(response.url, 'after', data[dataLength - 1]["id"])
            url = re.sub(r"([0-9]){1,9}(?=\?)", str(page), url) # build the new url
            yield scrapy.Request(url, self.parse_page) #save data
            yield scrapy.Request(url, self.parse) #do it again with the new url recursively, this bit doesn't work

        yield from self.parse_page(response) # do it for the first page

    def parse_page(self, response):
        data = json.loads(str(response.body, 'utf-8'))
        for item in data:
            finalData = {"language": item["languageName"]}
            yield finalData

在控制台scrapy runspider scrap_kaggle.py -o file.csv -t csv中运行只保存csv文件中的前2个网址。

1 个答案:

答案 0 :(得分:1)

首先,我想解释为什么你只得到两个回应。您有一个逻辑错误:在parse函数中,您初始化page=0,以便以下请求始终为page=20,不再包含任何页面。

然后我想给你一些建议,因为递归爬行的逻辑性有点大。根据{{​​3}}:

  

解析(响应)   这是Scrapy在其请求未指定回调时用于处理下载响应的默认回调。

     

解析方法负责处理响应并返回要删除的数据和/或更多URL。其他请求回调与Spider类具有相同的要求。

     

此方法以及任何其他请求回调 必须返回可迭代的Request和/或dicts或Item对象

(注意我标记的粗体部分)。这意味着parse函数本身可以执行两个操作:解析项目(在您的情况下保存操作)并跟随下一个请求(在您的情况下,递归操作)。因此,您不需要两个不同的parse函数。

最后,我想提供一个伪代码来解释这个想法。请确保可以正确构建下一个请求URL。我无法测试您负责的代码:

import scrapy
import json
import logging
from w3lib.url import add_or_replace_parameter
import re
class MySpider(scrapy.Spider):
    name = 'myspider'
    start_urls = ['https://www.example/0/1234']

    def parse(self, response): # check if there is an id
        data = json.loads(str(response.body, 'utf-8'))
        # SAVE ITEMS OPERATION
        for item in data:
            finalData = {"language": item["languageName"]}
            yield finalData

        dataLength = len(data)
        # YOU NEED TO DETERMINE THE PAGE NUMBER TO BUILD NEXT REQUEST URL
        page = GET_PREV_PAGE #get the previous page number by response.url
        if data[dataLength - 1]["id"]:
            page += 20
            url = add_or_replace_parameter(response.url, 'after', data[dataLength - 1]["id"])
            url = re.sub(r"([0-9]){1,9}(?=\?)", str(page), url) # build the new url
            # SEND NEXT REQUEST, THE RECURSION OPERATION
            yield scrapy.Request(url, self.parse)

如您所见,保存操作和后续操作都可以放在一个parse函数中。以下操作中最重要的部分是确定下一个请求的URL。对于您的情况,您可以通过解析响应数据和响应URL来确定它。如果您需要更多信息来确定下一个网址,可以将这些数据放入meta请求中,然后通过meta响应进行检索,请检查scrapy spider docs。例如,在产生请求时,您可以:

yield Request(url, meta=dict(page=CURRENT_PAGE_ID)

在您的parse函数中,处理响应时,您可以检索此元数据以构建下一个请求:

page = response.meta['page']
next_page = page + 20

这仅适用于复杂的情况。在你的情况下,页面很容易获得,你不需要。

希望这会有所帮助。 感谢。