无法在单独的方法中同时打印三个字段
Can't print three fields at the same time within a separate method
我创建了一个蜘蛛程序来从网站上获取不同项目的名称、描述和状态。蜘蛛可以从一个地方抓取名称和描述,但它必须去同一站点内的另一个地方获取状态。
以下是代表如何手动执行整个操作的步骤,这也有助于理解脚本的构建逻辑。
- 导航到 this link and parse the
ids
and links connected to ids
from here。
- 使用
ids
生成 json 响应,其中所需状态可用。
- 使用
links connected to ids
从内页解析名称和描述,如this one。
只要它以两种不同的方法打印所需信息,蜘蛛就做得很好 fetch_status()
和 fetch_content()
。
到目前为止我已经尝试过:
import json
import scrapy
import urllib
from bs4 import BeautifulSoup
class OmronSpider(scrapy.Spider):
name = 'omron'
start_urls = ['https://industrial.omron.de/de/products/nyb']
status_url = 'https://industrial.omron.de/en/api/product_lifecycle_management/search?'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36',
'Accept': '*/*'
}
def parse(self, response):
soup = BeautifulSoup(response.text,"lxml")
for item in soup.select("table.details > tbody > tr.filtered > td.product-name a"):
product_id = item.get_text(strip=True)
product_url = response.urljoin(item.get("href"))
yield scrapy.Request(product_url,headers=self.headers,callback=self.fetch_content)
params = {'q': product_id}
req_url = f'{self.status_url}{urllib.parse.urlencode(params)}'
yield scrapy.Request(req_url,headers=self.headers,callback=self.fetch_status)
def fetch_status(self, response):
item = json.loads(response.text)['data']
if item:
yield {"status":item[0]['status']}
else:
yield {"status":None}
def fetch_content(self, response):
soup = BeautifulSoup(response.text,"lxml")
product_name = soup.select_one("header.page-width > h1").get_text(strip=True)
description = soup.select_one(".content > p").get_text(strip=True)
yield {"product_name":product_name,"description":description}
如何打印三个字段,如 product_name
、description
和 status
可能在单独的同一时间方法?
您可以使用 cb_kwargs
将数据从一个回调传递到另一个回调。特别针对您的情况,您希望将已解析的数据(product_name
和 description
)传递给您的 fetch_status
方法。
下面是一个例子(在测试中我看到 status
总是 None
所以不确定是否需要进一步调试)。
我更改了一些额外的东西:
- 使用内置的
TextResponse.css
方法来select内容而不是使用BeautifulSoup
(如果你觉得更舒服的话,使用bs4
是可以的,但是scrapy
有你需要的这个例子)
- 使用
Response.follow
而不是手工制作新的 Request
(有一些优点,例如能够直接使用 Selector
,就像我在 parse
方法)
- 使用
TextResponse.json
反序列化 json(只是一个快捷方式,而不是导入 json
模块`)
import scrapy
import urllib
class OmronSpider(scrapy.Spider):
name = "omron"
start_urls = ["https://industrial.omron.de/de/products/nyb"]
status_url = (
"https://industrial.omron.de/en/api/product_lifecycle_management/search?"
)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36",
"Accept": "*/*",
}
def parse(self, response):
for item in response.css("td.product-name a"):
yield response.follow(item, callback=self.fetch_content)
def fetch_content(self, response):
parsed_data = {
"product_name": response.css("header.page-width > h1::text").get(),
"description": response.css(".content > p::text").get(),
}
params = {"q": parsed_data["product_name"]}
yield response.follow(
f"{self.status_url}{urllib.parse.urlencode(params)}",
callback=self.fetch_status,
cb_kwargs=dict(parsed_data=parsed_data),
headers=self.headers,
)
def fetch_status(self, response, parsed_data):
item = response.json()["data"]
status = item[0]["status"] if item else None
yield {**parsed_data, "status": status}
我创建了一个蜘蛛程序来从网站上获取不同项目的名称、描述和状态。蜘蛛可以从一个地方抓取名称和描述,但它必须去同一站点内的另一个地方获取状态。
以下是代表如何手动执行整个操作的步骤,这也有助于理解脚本的构建逻辑。
- 导航到 this link and parse the
ids
andlinks connected to ids
from here。 - 使用
ids
生成 json 响应,其中所需状态可用。 - 使用
links connected to ids
从内页解析名称和描述,如this one。
只要它以两种不同的方法打印所需信息,蜘蛛就做得很好 fetch_status()
和 fetch_content()
。
到目前为止我已经尝试过:
import json
import scrapy
import urllib
from bs4 import BeautifulSoup
class OmronSpider(scrapy.Spider):
name = 'omron'
start_urls = ['https://industrial.omron.de/de/products/nyb']
status_url = 'https://industrial.omron.de/en/api/product_lifecycle_management/search?'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36',
'Accept': '*/*'
}
def parse(self, response):
soup = BeautifulSoup(response.text,"lxml")
for item in soup.select("table.details > tbody > tr.filtered > td.product-name a"):
product_id = item.get_text(strip=True)
product_url = response.urljoin(item.get("href"))
yield scrapy.Request(product_url,headers=self.headers,callback=self.fetch_content)
params = {'q': product_id}
req_url = f'{self.status_url}{urllib.parse.urlencode(params)}'
yield scrapy.Request(req_url,headers=self.headers,callback=self.fetch_status)
def fetch_status(self, response):
item = json.loads(response.text)['data']
if item:
yield {"status":item[0]['status']}
else:
yield {"status":None}
def fetch_content(self, response):
soup = BeautifulSoup(response.text,"lxml")
product_name = soup.select_one("header.page-width > h1").get_text(strip=True)
description = soup.select_one(".content > p").get_text(strip=True)
yield {"product_name":product_name,"description":description}
如何打印三个字段,如 product_name
、description
和 status
可能在单独的同一时间方法?
您可以使用 cb_kwargs
将数据从一个回调传递到另一个回调。特别针对您的情况,您希望将已解析的数据(product_name
和 description
)传递给您的 fetch_status
方法。
下面是一个例子(在测试中我看到 status
总是 None
所以不确定是否需要进一步调试)。
我更改了一些额外的东西:
- 使用内置的
TextResponse.css
方法来select内容而不是使用BeautifulSoup
(如果你觉得更舒服的话,使用bs4
是可以的,但是scrapy
有你需要的这个例子) - 使用
Response.follow
而不是手工制作新的Request
(有一些优点,例如能够直接使用Selector
,就像我在parse
方法) - 使用
TextResponse.json
反序列化 json(只是一个快捷方式,而不是导入json
模块`)
import scrapy
import urllib
class OmronSpider(scrapy.Spider):
name = "omron"
start_urls = ["https://industrial.omron.de/de/products/nyb"]
status_url = (
"https://industrial.omron.de/en/api/product_lifecycle_management/search?"
)
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36",
"Accept": "*/*",
}
def parse(self, response):
for item in response.css("td.product-name a"):
yield response.follow(item, callback=self.fetch_content)
def fetch_content(self, response):
parsed_data = {
"product_name": response.css("header.page-width > h1::text").get(),
"description": response.css(".content > p::text").get(),
}
params = {"q": parsed_data["product_name"]}
yield response.follow(
f"{self.status_url}{urllib.parse.urlencode(params)}",
callback=self.fetch_status,
cb_kwargs=dict(parsed_data=parsed_data),
headers=self.headers,
)
def fetch_status(self, response, parsed_data):
item = response.json()["data"]
status = item[0]["status"] if item else None
yield {**parsed_data, "status": status}