【问题标题】:scrape multiple addresses from multiple files in scrapy从scrapy中的多个文件中刮取多个地址
【发布时间】:2018-06-15 18:23:36
【问题描述】:

我在一个目录中有一些 JSON 文件。在任何这些文件中,都有一些我需要的信息。我需要的第一个属性是 scrapy 中“start_urls”的链接列表。

每个文件都用于不同的进程,因此它的输出必须是分开的。所以我不能把所有json文件中的所有链接都放到start_urls中一起运行。我必须为每个文件运行蜘蛛。

我该怎么做?到目前为止,这是我的代码:

import scrapy
from os import listdir
from os.path import isfile, join
import json
class HotelInfoSpider(scrapy.Spider):
    name = 'hotel_info'
    allowed_domains = ['lastsecond.ir']
    # get start urls from links list of every file
    files = [f for f in listdir('lastsecond/hotels/') if 
    isfile(join('lastsecond/hotels/', f))]
    with open('lastsecond/hotels/' + files[0], 'r') as hotel_info:
        hotel = json.load(hotel_info)
    start_urls = hotel["links"]

    def parse(self, response):
        print("all good")

【问题讨论】:

  • 您可以使用Request(..., meta={'info':...})start_requests() 中创建start_urls,您可以在每个请求中添加额外的info,它可以将此info 发送到您自己的管道导出器,它将使用它info 将结果保存到不同的文件中。

标签: python json file scrapy


【解决方案1】:

我看到了两种方法


第一:

使用不同的参数多次运行蜘蛛。它将需要更少的代码。

您可以使用手动添加不同参数的多行创建批处理。

第一个参数是输出文件名-o result1.csv,scrapy 会自动创建。
第二个参数是带有链接的输入文件名-a filename=process1.csv

scrapy crawl hotel_info -o result1.csv -a filename=process1.csv
scrapy crawl hotel_info -o result2.csv -a filename=process2.csv
scrapy crawl hotel_info -o result3.csv -a filename=process3.csv
...

a 它只需要在__init__ 中获取filename

import scrapy
from os.path import isfile, join
import json

class HotelInfoSpider(scrapy.Spider):

    name = 'hotel_info'

    allowed_domains = ['lastsecond.ir']

    def __init__(self, filename, *args, **kwargs): # <-- filename
        super().__init__(*args, **kwargs)

        filename = join('lastsecond/hotels/', filename) 

        if isfile(filename):
            with open(filename) as f:
                data = json.load(f)
                self.start_urls = data['links']

    def parse(self, response):
        print('url:', response.url)

        yield {'url':, response.url, 'other': ...}

您还可以使用带有CrawlerProcess 的 Python 脚本多次运行蜘蛛。

from scrapy.crawler import CrawlerProcess
import HotelInfoSpider
from os.path import isfile, join
import json

files = [f for f in listdir('lastsecond/hotels/') if isfile(join('lastsecond/hotels/', f))]

for i, input_file in enumerate(files):
    output_file = 'result{}.csv'.format(i)
    c = CrawlerProcess({'FEED_FORMAT': 'csv','FEED_URI': output_file})
    c.crawl(HotelInfoSpider, filename=input_file) #input_file='process1.csv')
    c.start()

或使用scrapy.cmdline.execute()

import scrapy.cmdline
from os.path import isfile, join
import json

files = [f for f in listdir('lastsecond/hotels/') if isfile(join('lastsecond/hotels/', f))]

for i, input_file in enumerate(files):
    output_file = 'result{}.csv'.format(i)
    scrapy.cmdline.execute(["scrapy", "crawl", "hotel_info", "-o", output_file, "-a" "filename=" + input_file])

第二:

它需要更多代码,因为您必须创建将使用不同文件来保存结果的 Pipeline Exporter。

您必须使用start_requests()Request(..., meta=...) 来创建start_urls 请求,这些请求将在meta 中包含extra 数据,您以后可以使用这些数据保存在不同的文件中。

parse() 中,您必须从meta 获取此extra 并添加到item

在管道导出器中,您必须从 item 获取 extra 并打开不同的文件。

import scrapy
from os import listdir
from os.path import isfile, join
import json

class HotelInfoSpider(scrapy.Spider):

    name = 'hotel_info'

    allowed_domains = ['lastsecond.ir']

    def start_requests(self):

        # get start urls from links list of every file
        files = [f for f in listdir('lastsecond/hotels/') if isfile(join('lastsecond/hotels/', f))]

        for i, filename in enumerate(files):
            with open('lastsecond/hotels/' + filename) as f:
                data = json.load(f)
                links = data["links"]
                for url in links:
                    yield scrapy.Request(url, meta={'extra': i})

    def parse(self, response):
        print('url:', response.url)
        extra = response.meta['extra']
        print('extra:', extra)

        yield {'url': response.url, 'extra': extra, 'other': ...}

管道.py

class MyExportPipeline(object):

    def process_item(self, item, spider):

        # get extra and use it in filename
        filename = 'result{}.csv'.format(item['extra'])

        # open file for appending
        with open(filename, 'a') as f:
            writer = csv.writer(f)

            # write only selected elements - skip `extra`
            row = [item['url'], item['other']
            writer.writerow(row)

        return item

settings.py

ITEM_PIPELINES = {
   'your_porject_name.pipelines.MyExportPipeline': 300,
}

【讨论】:

  • 再次感谢您的回答。我用额外的函数编写了这段代码,但管道只在我使用的最后一个文件上工作。我有几个文件,里面有几个链接,但是管道只保存最后一个文件的 url 信息。
  • 例如,您必须指定请求信息来自该链接来自哪个文件。然后你必须使用这些信息保存在不同的输出文件中。
  • 解析函数中的“yield”有什么作用?我必须用 css 收集一些信息并将它们添加到项目中。但是我怎样才能通过“yield”传递它们?
  • yield 就像return - 它返回值和冻结函数。当scrapy 再次执行parse() 时,它将从同一个地方开始——如果你必须返回许多项目和Requests,它在parser()` 中很有用。 return 返回值,但是当scrapy 再次执行parse() 时,它将从头开始。如果您有很多元素,您必须使用您的信息和yieldfor 循环中创建字典,如果您只有一项,则必须在return 中创建。请参阅在线词典yield {'url': response.url, 'extra': extra, 'other': ...}
  • 如果您使用类获取信息(即YourItem(scrapy.Item)),那么您必须为extra 信息添加字段,然后生成您的类的实例 - item = YourItem() ; item['extra'] = ... ; yield item
【解决方案2】:

您可以使用dict 管理所有文件:

d_hotel_info = {}
for file in files:
    with open('lastsecond/hotels/' + file, 'r') as hotel_info:
        hotel = json.load(hotel_info)
    d_hotel_info[file] = hotel

然后当你想输出的时候,你引用d_hotel_info的键

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多