选择动态加载的内容¶
某些网页在Web浏览器中加载时会显示所需的数据。但是,当您使用scrappy下载它们时,您无法使用 selectors .
当这种情况发生时,建议的方法是 find the data source 从中提取数据。
如果您未能做到这一点,并且仍然可以通过 DOM 从Web浏览器中,请参见 预渲染JavaScript .
查找数据源¶
要提取所需的数据,必须首先找到其源位置。
如果数据是非基于文本的格式,如图像或PDF文档,请使用 network tool 找到相应的请求,以及 reproduce it .
如果您的Web浏览器允许您选择所需的数据作为文本,则数据可以在嵌入的javascript代码中定义,也可以从基于文本格式的外部资源加载。
在这种情况下,您可以使用类似 wgrep 以查找该资源的URL。
如果数据原来来自原始URL本身,则必须 inspect the source code of the webpage 以确定数据的位置。
如果数据来自不同的URL,则需要 reproduce the corresponding request .
检查网页的源代码¶
有时您需要检查网页的源代码(而不是 DOM )确定所需数据的位置。
使用Scrapy's fetch
命令下载Scrapy看到的网页内容:
scrapy fetch --nolog https://example.com > response.html
如果所需数据位于 <script/>
元素,请参见 分析javascript代码 .
如果你找不到想要的数据,首先要确保它不仅仅是垃圾:用HTTP客户端下载网页,比如 curl 或 wget 看看这些信息是否可以在他们得到的响应中找到。
如果他们得到了所需数据的响应,请修改您的Scrapy Request
以匹配另一个HTTP客户端。例如,尝试使用相同的用户代理字符串 (USER_AGENT
)或相同 headers
。
如果他们也得到了没有所需数据的响应,那么您需要采取措施使您的请求更类似于Web浏览器的请求。见 复制请求 .
复制请求¶
有时,我们需要以Web浏览器执行请求的方式重新生成请求。
使用 network tool 查看Web浏览器如何执行所需的请求,并尝试用scrapy重新生成该请求。
这可能足以产生一个 Request
使用相同的HTTP方法和URL。但是,您可能还需要重新生成正文、标头和表单参数(请参见 FormRequest
)。
因为所有主要浏览器都允许将请求导出到 cURL 格式,Scrapy结合了该方法 from_curl()
要生成等效项,请执行以下操作 Request
来自卷曲命令。要获取更多信息,请访问 request from curl 在网络工具部分内部。
一旦得到预期的响应,您就可以 extract the desired data from it .
你可以用Scrapy复制任何请求。但是,有时复制所有必需的请求在开发人员时间内似乎不高效。如果是这样,爬行速度对你来说不是主要的问题,你也可以考虑 JavaScript pre-rendering .
如果你得到预期的答复 sometimes 但并非总是这样,问题可能不是您的请求,而是目标服务器。目标服务器可能有问题、过载或 banning 你的一些要求。
注意,要将cURL命令转换为Scrapy请求,可以使用 curl2scrapy .
处理不同的响应格式¶
一旦对所需数据进行响应,如何从中提取所需数据取决于响应类型:
如果响应是HTML或XML,请使用 selectors 像往常一样。
如果响应是json,则使用
json.loads()
从中加载所需数据response.text
::data = json.loads(response.text)
如果所需数据位于嵌入在JSON数据中的HTML或XML代码中,则可以将该HTML或XML代码加载到
Selector
然后 use it 一如既往::selector = Selector(data['html'])
如果响应是javascript,或HTML
<script/>
包含所需数据的元素,请参见 分析javascript代码 .如果响应是css,请使用 regular expression 从中提取所需数据
response.text
.
如果响应是基于图像的图像或其他格式(例如PDF),则从
response.body
并使用OCR解决方案将所需数据提取为文本。例如,您可以使用 pytesseract. 要从PDF中读取表格, tabula-py 可能是更好的选择。
如果响应是SVG,或者带有包含所需数据的嵌入式SVG的HTML,则可以使用 selectors ,因为SVG是基于XML的。
否则,可能需要将SVG代码转换为栅格图像,并且 handle that raster image .
分析javascript代码¶
如果所需数据是用javascript硬编码的,则首先需要获取javascript代码:
如果javascript代码在javascript文件中,只需读取
response.text
.如果javascript代码在
<script/>
HTML页的元素,使用 selectors 提取其中的文本<script/>
元素。
一旦有了包含javascript代码的字符串,就可以从中提取所需的数据:
你可能会使用 regular expression 以JSON格式提取所需数据,然后可以使用
json.loads()
.例如,如果javascript代码包含类似
var data = {{"field": "value"}};
您可以按如下方式提取该数据:>>> pattern = r'\bvar\s+data\s*=\s*(\{.*?\})\s*;\s*\n' >>> json_data = response.css('script::text').re_first(pattern) >>> json.loads(json_data) {'field': 'value'}
chompjs 提供将JavaScript对象解析为
dict
.例如,如果javascript代码包含
var data = {{field: "value", secondField: "second value"}};
您可以按如下方式提取该数据:>>> import chompjs >>> javascript = response.css('script::text').get() >>> data = chompjs.parse_js_object(javascript) >>> data {'field': 'value', 'secondField': 'second value'}
否则,使用 js2xml 要将javascript代码转换为XML文档,可以使用 selectors .
例如,如果javascript代码包含
var data = {{field: "value"}};
您可以按如下方式提取该数据:>>> import js2xml >>> import lxml.etree >>> from parsel import Selector >>> javascript = response.css('script::text').get() >>> xml = lxml.etree.tostring(js2xml.parse(javascript), encoding='unicode') >>> selector = Selector(text=xml) >>> selector.css('var[name="data"]').get() '<var name="data"><object><property name="field"><string>value</string></property></object></var>'
预渲染JavaScript¶
在从其他请求中获取数据的网页上,复制包含所需数据的请求是首选方法。这项工作通常是值得的:结构化的、完整的数据,最少的解析时间和网络传输。
然而,有时很难重现某些请求。或者你可能需要一些没有请求可以提供给你的东西,比如网页的屏幕截图,就像在网页浏览器中看到的那样。
在这些情况下,使用 Splash JavaScript呈现服务,以及 scrapy-splash 实现无缝集成。
splash返回为html DOM 一个网页,这样你就可以用 selectors . 它通过 configuration 或 scripting.
如果您需要Splash提供的以外的东西,例如从python代码即时与DOM交互而不是使用以前编写的脚本,或者处理多个Web浏览器窗口,您可能需要 use a headless browser 相反。
使用无头浏览器¶
A headless browser 是一种特殊的Web浏览器,它为自动化提供API。通过安装 asyncio reactor ,则可以集成 asyncio
基于库,用于处理无头浏览器。
其中一个这样的库是 playwright-python (Python官方端口 playwright )。下面是一个简单的代码片段,用来说明它在Scrapy爬行器中的用法:
import scrapy
from playwright.async_api import async_playwright
class PlaywrightSpider(scrapy.Spider):
name = "playwright"
start_urls = ["data:,"] # avoid using the default Scrapy downloader
async def parse(self, response):
async with async_playwright() as pw:
browser = await pw.chromium.launch()
page = await browser.new_page()
await page.goto("https:/example.org")
title = await page.title()
return {"title": title}
但是,使用 playwright-python 与上面的示例一样,直接绕过了大多数scrapy组件(中间件、dupefilter等)。我们建议您使用 scrapy-playwright 为了更好的整合。