Post 在主页上用 scrapy 请求 ajax
Post request with scrapy on homepage with ajax
我正试图在网站上抓取各种药店的价格 https://www.medizinfuchs.de for a specific drug (e.g., https://www.medizinfuchs.de/preisvergleich/aspirin-complex-beutel-20-st-bayer-vital-gmbh-pzn-4114918.html)。
该页面使用通过加载更多按钮调用的无限滚动。使用开发者工具的网络分析,我看到如果我点击这个按钮,页面会向 https://www.medizinfuchs.de/ajax_apotheken 发送 post 请求。
如果我将此 post 请求复制为 cURL,然后使用 curl2scrapy 将其转换,我会得到以下代码:
from scrapy import Request
url = 'https://www.medizinfuchs.de/ajax_apotheken"'
request = Request(
url=url,
method='POST',
dont_filter=True,
)
fetch(request)
网络分析显示post请求的响应是HTML格式(类似于主页),但是列出了所有家药店那里有他们的价格(在我点击加载更多按钮之前,不仅仅是主页上的十家药店)。
我有点尴尬的问题 - 我仍然是一个绝对的初学者 - 现在我如何将这个 post 请求整合到我以前的 python 代码中,以便扫描所有药店并获得价格所有药房的信息。我之前的python代码是:
import scrapy
class MedizinfuchsSpider(scrapy.Spider):
name = "medizinfuchs"
start_urls = [
'https://www.medizinfuchs.de/preisvergleich/aspirin-complex-beutel-20-st-bayer-vital-gmbh-pzn-4114918.html'
]
def parse(self, response):
for apotheke in response.css('div.apotheke'):
yield {
'name': apotheke.css('a.name::text').getall(),
'single': apotheke.css('div.single::text').getall(),
'shipping': apotheke.css('div.shipping::text').getall(),
}
非常感谢您的支持:-)。
基督教徒
如果您愿意接受仅使用请求和 beautifulsoup 的建议,您可以:
- 使用
requests.Session()
存储 cookie 并在 url s.get(url)
上执行第一次调用。这将获得等于产品 ID 的 cookie product_history
- 使用
requests.post
调用您在 chrome 开发工具中发现的 API,并在数据 [=33= 表单中指定 id
]
以下示例迭代产品列表并执行上述流程:
import requests
from bs4 import BeautifulSoup
import pandas as pd
products = [
"https://www.medizinfuchs.de/preisvergleich/aspirin-complex-beutel-20-st-bayer-vital-gmbh-pzn-4114918.html",
"https://www.medizinfuchs.de/preisvergleich/alcohol-pads-b.braun-100-st-b.-braun-melsungen-ag-pzn-629703.html"
]
results = []
for url in products:
# get id
s = requests.Session()
r = s.get(url)
id = s.cookies.get_dict()["product_history"]
soup = BeautifulSoup(r.text, "html.parser")
pzn = soup.find("li", {"class": "pzn"}).text[5:]
print(f'pzn: {pzn}')
# make the call
r = requests.post("https://www.medizinfuchs.de/ajax_apotheken",
data={
"params[ppn]": id,
"params[entry_order]": "single_asc",
"params[filter][rating]": "",
"params[filter][country]": 7,
"params[filter][favorit]": 0,
"params[filter][products_from][de]": 0,
"params[filter][products_from][at]": 0,
"params[filter][send]": 1,
"params[limit]": 300,
"params[merkzettel_sel]": "",
"params[merkzettel_reload]": "",
"params[apo_id]": ""
})
soup = BeautifulSoup(r.text, "html.parser")
data = [
{
"name": t.find("a").text.strip(),
"single": t.find("div", {"class": "single"}).text.strip(),
"shipping": t.find("div", {"class": "shipping"}).text.strip().replace("\t", "").replace("\n", " "),
}
for t in soup.findAll("div", {"class": "apotheke"})
]
for t in data:
results.append({
"pzn": pzn,
**t
})
df = pd.DataFrame(results)
df.to_csv('result.csv', index=False)
print(df)
repl.it: https://replit.com/@bertrandmartel/ScrapeMedicinFuchs
请注意,在上述解决方案中,我仅使用 requests.Session()
来获取 product_history
cookie。后续调用不需要该会话。这样,我就可以直接获取产品 ID,而无需在 html/js 中使用正则表达式。但也许有更好的方法来获取产品 ID,我们无法从 url 中获取它,因为它只有部分产品 ID 4114918
而不是 1104114918
(如果你不想对 110
后缀部分进行硬编码)
我正试图在网站上抓取各种药店的价格 https://www.medizinfuchs.de for a specific drug (e.g., https://www.medizinfuchs.de/preisvergleich/aspirin-complex-beutel-20-st-bayer-vital-gmbh-pzn-4114918.html)。
该页面使用通过加载更多按钮调用的无限滚动。使用开发者工具的网络分析,我看到如果我点击这个按钮,页面会向 https://www.medizinfuchs.de/ajax_apotheken 发送 post 请求。
如果我将此 post 请求复制为 cURL,然后使用 curl2scrapy 将其转换,我会得到以下代码:
from scrapy import Request
url = 'https://www.medizinfuchs.de/ajax_apotheken"'
request = Request(
url=url,
method='POST',
dont_filter=True,
)
fetch(request)
网络分析显示post请求的响应是HTML格式(类似于主页),但是列出了所有家药店那里有他们的价格(在我点击加载更多按钮之前,不仅仅是主页上的十家药店)。
我有点尴尬的问题 - 我仍然是一个绝对的初学者 - 现在我如何将这个 post 请求整合到我以前的 python 代码中,以便扫描所有药店并获得价格所有药房的信息。我之前的python代码是:
import scrapy
class MedizinfuchsSpider(scrapy.Spider):
name = "medizinfuchs"
start_urls = [
'https://www.medizinfuchs.de/preisvergleich/aspirin-complex-beutel-20-st-bayer-vital-gmbh-pzn-4114918.html'
]
def parse(self, response):
for apotheke in response.css('div.apotheke'):
yield {
'name': apotheke.css('a.name::text').getall(),
'single': apotheke.css('div.single::text').getall(),
'shipping': apotheke.css('div.shipping::text').getall(),
}
非常感谢您的支持:-)。
基督教徒
如果您愿意接受仅使用请求和 beautifulsoup 的建议,您可以:
- 使用
requests.Session()
存储 cookie 并在 urls.get(url)
上执行第一次调用。这将获得等于产品 ID 的 cookie - 使用
requests.post
调用您在 chrome 开发工具中发现的 API,并在数据 [=33= 表单中指定id
]
product_history
以下示例迭代产品列表并执行上述流程:
import requests
from bs4 import BeautifulSoup
import pandas as pd
products = [
"https://www.medizinfuchs.de/preisvergleich/aspirin-complex-beutel-20-st-bayer-vital-gmbh-pzn-4114918.html",
"https://www.medizinfuchs.de/preisvergleich/alcohol-pads-b.braun-100-st-b.-braun-melsungen-ag-pzn-629703.html"
]
results = []
for url in products:
# get id
s = requests.Session()
r = s.get(url)
id = s.cookies.get_dict()["product_history"]
soup = BeautifulSoup(r.text, "html.parser")
pzn = soup.find("li", {"class": "pzn"}).text[5:]
print(f'pzn: {pzn}')
# make the call
r = requests.post("https://www.medizinfuchs.de/ajax_apotheken",
data={
"params[ppn]": id,
"params[entry_order]": "single_asc",
"params[filter][rating]": "",
"params[filter][country]": 7,
"params[filter][favorit]": 0,
"params[filter][products_from][de]": 0,
"params[filter][products_from][at]": 0,
"params[filter][send]": 1,
"params[limit]": 300,
"params[merkzettel_sel]": "",
"params[merkzettel_reload]": "",
"params[apo_id]": ""
})
soup = BeautifulSoup(r.text, "html.parser")
data = [
{
"name": t.find("a").text.strip(),
"single": t.find("div", {"class": "single"}).text.strip(),
"shipping": t.find("div", {"class": "shipping"}).text.strip().replace("\t", "").replace("\n", " "),
}
for t in soup.findAll("div", {"class": "apotheke"})
]
for t in data:
results.append({
"pzn": pzn,
**t
})
df = pd.DataFrame(results)
df.to_csv('result.csv', index=False)
print(df)
repl.it: https://replit.com/@bertrandmartel/ScrapeMedicinFuchs
请注意,在上述解决方案中,我仅使用 requests.Session()
来获取 product_history
cookie。后续调用不需要该会话。这样,我就可以直接获取产品 ID,而无需在 html/js 中使用正则表达式。但也许有更好的方法来获取产品 ID,我们无法从 url 中获取它,因为它只有部分产品 ID 4114918
而不是 1104114918
(如果你不想对 110
后缀部分进行硬编码)