选择动态加载的内容

某些网页在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客户端下载网页,比如 curlwget 看看这些信息是否可以在他们得到的响应中找到。

如果他们得到了所需数据的响应,请修改您的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 . 它通过 configurationscripting.

如果您需要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 为了更好的整合。