Python 在scrapy中有多个请求时存储项目

Python 在scrapy中有多个请求时存储项目,python,web-scraping,scrapy,Python,Web Scraping,Scrapy,我有一个项对象,我需要从方法phoneParse中取出该对象,并将其值与我已经加载的其他值一起加载 我试图从回调函数中获取电话号码,并在生成json或csv文件时以这种方式将其添加到加载程序中 loader.add_css("features", '.offer-features__item::text') loader.add_value('url', response.url) 以及数字的数据 我如何实现这样的功能 import scrapy import time import json

我有一个对象,我需要从方法phoneParse中取出该对象,并将其值与我已经加载的其他值一起加载

我试图从回调函数中获取电话号码,并在生成json或csv文件时以这种方式将其添加到加载程序中

loader.add_css("features", '.offer-features__item::text')
loader.add_value('url', response.url)
以及数字的数据

我如何实现这样的功能

import scrapy
import time
import json


from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose
from scrapy.shell import inspect_response

from otomoto.items import OtomotoItem


def filter_out_array(x):
    x = x.strip()
    return None if x == '' else x


class OtomotoCarLoader(ItemLoader):
    default_output_processor = TakeFirst()

    features_out = MapCompose(filter_out_array)


class OtomotoSpider(scrapy.Spider):

    name = 'otomoto'
    start_urls = ['https://www.otomoto.pl/osobowe/']

    def parse(self, response):
        for car_page in response.css('.offer-title__link::attr(href)'):
            yield response.follow(car_page, self.parse_car_page)

        for next_page in response.css('.next.abs a::attr(href)'):
            yield response.follow(next_page, self.parse)
########################## the function added to get the phone number ################

    def parse_number(self, response):

        #raw_data = response.url.body
        #for params in raw_data:
        number_id = response.xpath('//a[@data-path="multi_phone"]/@data-id').extract()
        print("NUMBER", number_id)
        number_id = list(dict.fromkeys(number_id))
        #number_id = response.css('a::attr(data-id)' and 'a::attrdata-path="multi_phone")
        print("NUMBER", number_id)
        return number_id

        #loader.add('number', number)
################################################################################

    def phoneParse(self, response):
        print("Res",response)
        item = response.xpath('//p/text()').extract()
        print(type(item))
        print(item)
    # HERE YOU NEED TO ITERATE IF YOU NEED ALL NUMBERS... but i think mostly they are the same, sometimes there are 2
        json_acceptable_string = item[0].replace("'", "\"")
        number_item_dict = json.loads(json_acceptable_string)
        print("RES2",number_item_dict["value"].replace(" ","")) # THERE IT IS AS STRING
        time.sleep(10)
        return item
    def parse_car_page(self, response):
        number_id = self.parse_number(response)
        for id in number_id:
            phone_url = "https://www.otomoto.pl/ajax/misc/contact/multi_phone/" + id + '/0/'
            print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA URL : "+phone_url)
            request = scrapy.Request(phone_url, callback=self.phoneParse)
            print(request)
        yield request

            #number = response.body
            #print('NUMBER',number)

            #<span class="objectBox objectBox-string">"725 169 377"</span>
            #exit()
        property_list_map = {
            'Marka pojazdu': 'brand',
            'Model pojazdu': 'model',
            'Rok produkcji': 'year',

        }
        loader = OtomotoCarLoader(OtomotoItem(), response=response)

        for params in response.css('.offer-params__item'):

            property_name = params.css(
                '.offer-params__label::text').extract_first().strip()
            if property_name in property_list_map:
                css = params.css('div::text').extract_first().strip()
                if css == '':
                    css = params.css('a::text').extract_first().strip()

                loader.add_value(property_list_map[property_name], css)

        loader.add_css('features', '.offer-features__item::text')
        loader.add_value('url', response.url)
        #loader.add_value('number', response.number)
        yield loader.load_item()
import scrapy
导入时间
导入json
从scrapy.loader导入ItemLoader
从scrapy.loader.processors导入TakeFirst,MapCompose
从scrapy.shell导入检查\u响应
从otomoto.items导入OtomotoItem
def过滤器输出阵列(x):
x=x.条带()
如果x==''或x,则返回None
装载机类别(项目装载机):
默认输出处理器=TakeFirst()
features\u out=MapCompose(过滤器\u out\u数组)
蜘蛛类(刮痕蜘蛛):
名称='otomoto'
起始URL=['https://www.otomoto.pl/osobowe/']
def解析(自我,响应):
对于响应中的car_页面.css('.offer-title_链接::attr(href'):
屈服响应。跟随(car\u页面,self.parse\u car\u页面)
对于response.css('.next.abs a::attr(href')中的下一页:
生成响应。follow(下一页,self.parse)
##########################添加的用于获取电话号码的功能################
def解析_编号(自身、响应):
#原始数据=response.url.body
#对于原始数据中的参数:
number\u id=response.xpath('//a[@data path=“multi\u phone”]/@data id').extract()
打印(“编号”,编号\u id)
number\u id=列表(dict.fromkeys(number\u id))
#number\u id=response.css('a::attr(数据id)'和'a::attr data path=“multi\u phone”)
打印(“编号”,编号\u id)
返回编号\u id
#加载器。添加('number',number)
################################################################################
def phoneParse(自我,响应):
打印(“Res”,回复)
item=response.xpath(“//p/text()”).extract()
打印(类型(项目))
打印(项目)
#在这里,如果需要所有数字,您需要迭代。。。但我认为大多数情况下它们是一样的,有时有两个
json\u可接受\u字符串=项[0]。替换(“'”,“\”)
number\u item\u dict=json.load(json\u可接受的字符串)
打印(“RES2”,编号项目内容[“值])。替换(“,”)#它以字符串形式出现
时间。睡眠(10)
退货项目
def解析车页面(自我,响应):
number\u id=self.parse\u number(响应)
对于编号为\u id的id:
电话地址=”https://www.otomoto.pl/ajax/misc/contact/multi_phone/“+id+”/0/'
打印(“aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
request=scrapy.request(电话\ url,callback=self.phoneParse)
打印(请求)
让步请求
#数字=响应。正文
#打印('数字',数字)
#"725 169 377"
#退出()
属性列表映射={
“Marka pojazdu”:品牌,
“模型pojazdu”:“模型”,
"韩国产品":"年",,
}
loader=OtomotoCarLoader(OtomotoItem(),response=response)
对于response.css('.offer-params__项')中的参数:
属性_name=params.css(
'.offer-params__标签::text').extract_first().strip()
如果属性列表映射中的属性名称:
css=params.css('div::text')。首先提取()
如果css='':
css=params.css('a::text')。首先提取()
loader.add_值(属性列表映射[属性名称],css)
loader.add_css('features','。offer-features__项::text')
loader.add_值('url',response.url)
#loader.add_值('number',response.number)
产量加载器。加载_项()

Scrapy
使用调度程序运行请求,所以当您使用
Request()
时,它会将其放入队列,稍后加载页面(当它有空闲的工作人员时,请参见:),所以它不会直接运行,并且您无法从
parsePhoto
parse\u car\u页面

您必须将数据从
parse\u car\u页面发送到
parsePhoto

  • parse\u car\u页面中
    解析页面上的所有数据
  • parse\u car\u页面中
    使用
    Request(…,meta=…)
    将这些数据(或
    loader
    )发送到
    parsePhoto

    yield scrapy.Request(phone_url, callback=self.phone_parse, meta={'loader': loader})
    
  • parsePhoto
    中获取这些数据

    loader = response.meta['loader']
    
  • parsePhoto
    中,刮取数字并
    生成所有数据


顺便说一句:
meta=
中,几乎可以使用任何键-但有些键有特殊含义:


完整的工作代码

您可以将它放在一个文件中,运行
pythonscript.py
,而无需创建项目。它将数据保存在
output.csv

import scrapy
from scrapy.loader import ItemLoader
from scrapy.loader.processors import TakeFirst, MapCompose
from scrapy.shell import inspect_response
import json
import time

class OtomotoItem(scrapy.Item):
    brand = scrapy.Field()
    model = scrapy.Field()
    year = scrapy.Field()
    url = scrapy.Field()
    number = scrapy.Field()
    features = scrapy.Field()


def filter_out_array(x):
    x = x.strip()
    return None if x == '' else x


class OtomotoCarLoader(ItemLoader):
    default_output_processor = TakeFirst()
    features_out = MapCompose(filter_out_array)


class OtomotoSpider(scrapy.Spider):

    name = 'otomoto'
    start_urls = ['https://www.otomoto.pl/osobowe/']

    def parse(self, response):

        for car_page in response.css('.offer-title__link::attr(href)'):
            yield response.follow(car_page, self.parse_car_page)

        for next_page in response.css('.next.abs a::attr(href)'):
            yield response.follow(next_page, self.parse)

    def parse_car_page(self, response):

        loader = OtomotoCarLoader(OtomotoItem(), response=response)

        property_list_map = {
            'Marka pojazdu': 'brand',
            'Model pojazdu': 'model',
            'Rok produkcji': 'year',
        }

        for params in response.css('.offer-params__item'):

            property_name = params.css('.offer-params__label::text').extract_first().strip()

            if property_name in property_list_map:
                css = params.css('div::text').extract_first().strip()

                if css == '':
                    css = params.css('a::text').extract_first().strip()

                loader.add_value(property_list_map[property_name], css)

        loader.add_css('features', '.offer-features__item::text')
        loader.add_value('url', response.url)

        number_id = self.parse_number(response)
        print('number_id:', len(number_id), '|', number_id)

        for id in number_id:
            phone_url = "https://www.otomoto.pl/ajax/misc/contact/multi_phone/" + id + '/0/'
            # use `meta=` to send data to `photo_parse`
            yield scrapy.Request(phone_url, callback=self.phone_parse, meta={'loader': loader})

    def parse_number(self, response):
        number_id = response.xpath('//a[@data-path="multi_phone"]/@data-id').extract()
        print("NUMBER [before]:", number_id)

        number_id = list(set(number_id))  # you can use `set()` to get unique values
        print("NUMBER [after] :", number_id)

        return number_id

    def phone_parse(self, response):
        print("[phone_parse] response:", response)

        # get data from `parse_car_page`
        loader = response.meta['loader']

        item = response.xpath('//p/text()').extract()
        print('[phone_parse] item:', type(item), item)

        json_data = json.loads(item[0])
        print('[phone_parse] json:', json_data)

        number = json_data["value"].replace(" ","")
        print("'[phone_parse] number:", number) # THERE IT IS AS STRING

        # add new data to loader
        loader.add_value('number', number)

        yield loader.load_item()

# --- run without project and save in `output.csv` ---

from scrapy.crawler import CrawlerProcess

c = CrawlerProcess({
    'USER_AGENT': 'Mozilla/5.0',
    # save in file CSV, JSON or XML
    'FEED_FORMAT': 'csv',     # csv, json, xml
    'FEED_URI': 'output.csv', #
})
c.crawl(OtomotoSpider)
c.start() 

你有一个JSON对象,你想填充它,然后一旦完成了抓取,就产生它?我不知道你想做什么,但通常你可以使用
for
-循环
yield
inside-ie
for number in list_of_numbers:yield{“number”:number}
您必须更好地描述问题。可能会显示一些示例数据和预期结果。hey@furas感谢您的回答。我编辑了我的原始anwser,试图退出回调函数并将号码添加到LoaderPlace。感谢您为这一响应投入的时间和精力。它清晰易懂顺便说一句:我在GitHub上保留了一些Stackoverflow中的示例:不同页面的示例和示例我一定会查看它们的。非常感谢much@Max有什么问题吗?我可以毫无问题地获得
Szczegóły
,但不需要使用
loader
使用
loade.ad的
项中的ds