Selecting dynamically-loaded content

当您将某些网页加载到Web浏览器中时,它们会显示所需的数据. 但是,当您使用Scrapy下载它们时,无法使用选择器到达所需的数据.

发生这种情况时,推荐的方法是找到数据源并从中提取数据.

If you fail to do that, and you can nonetheless access the desired data through the DOM from your web browser, see Pre-rendering JavaScript.

Finding the data source

要提取所需的数据,必须首先找到其源位置.

如果数据是非基于文本的格式(例如图像或PDF文档),请使用Web浏览器的网络工具查找相应的请求并进行再现 .

如果您的Web浏览器允许您选择所需的数据作为文本,则该数据可以用嵌入式JavaScript代码定义,或从外部资源以基于文本的格式加载.

在这种情况下,您可以使用wgrep之类的工具来查找该资源的URL.

如果事实证明数据来自原始URL本身,则必须检查网页的源代码以确定数据位于何处.

如果数据来自其他URL,则需要重现相应的request .

Inspecting the source code of a webpage

有时,您需要检查网页的源代码(而不是DOM )以确定某些所需数据的位置.

使用Scrapy的fetch命令下载Scrapy看到的网页内容:

scrapy fetch --nolog https://example.com > response.html

如果所需的数据在<script/>元素内的嵌入式JavaScript代码中,请参阅解析JavaScript代码 .

如果找不到所需的数据,请首先确保它不仅是Scrapy:使用HTTP客户端(例如curlwget)下载网页,然后查看在返回的响应中是否可以找到该信息.

如果他们收到所需数据的响应,请修改您的Scrapy Request以使其与其他HTTP客户端的Request相匹配. 例如,尝试使用相同的用户代理字符串( USER_AGENT )或相同的headers .

如果他们也收到没有所需数据的回复,则您需要采取措施使您的请求与网络浏览器的请求更加相似. 请参阅重现请求 .

Reproducing requests

有时我们需要以Web浏览器执行请求的方式来重现请求.

使用Web浏览器的网络工具查看Web浏览器如何执行所需的请求,并尝试使用Scrapy重现该请求.

产生具有相同HTTP方法和URL的Request可能就足够了. 但是,您可能还需要重现该请求的正文,标头和表单参数(请参见FormRequest ).

由于所有主流浏览器都允许以cURL格式导出请求,因此Scrapy合并了from_curl()方法,可通过cURL命令生成等效的Request . 要获取更多信息,请访问网络工具部分中curl的请求 .

获得预期的响应后,您可以从中提取所需的数据 .

您可以使用Scrapy重现任何请求. 但是,有时在开发人员看来,重现所有必要请求的效率似乎并不高. 如果是这种情况,并且爬网速度不是您的主要问题,则可以选择考虑JavaScript预渲染 .

如果有时 (但并非总是)获得预期的响应,则问题可能不是您的请求,而是目标服务器. 目标服务器可能有故障,超载或禁止了某些请求.

Handling different response formats

获得所需数据的响应后,如何从中提取所需数据取决于响应的类型:

  • 如果响应是HTML或XML,请照常使用选择器 .

  • 如果响应为JSON,请使用json.loadsresponse.text加载所需的数据:

    data = json.loads(response.text)
    

    如果所需的数据位于JSON数据中嵌入的HTML或XML代码内部,则可以将该HTML或XML代码加载到Selector ,然后照常使用它

    selector = Selector(data['html'])
    
  • If the response is JavaScript, or HTML with a <script/> element containing the desired data, see Parsing JavaScript code.

  • 如果响应是CSS,则使用正则表达式response.text提取所需的数据.

  • 如果响应是图像或基于图像的其他格式(例如PDF),请从response.body读取字节作为response.body并使用OCR解决方案将所需数据提取为文本.

    例如,您可以使用pytesseract . 要阅读从PDF表格, 塔布拉-PY可能是一个更好的选择.

  • 如果响应为SVG或包含所需数据的带有嵌入式SVG的HTML,则由于SVG基于XML,因此您可以使用选择器提取所需的数据.

    否则,您可能需要将SVG代码转换为光栅图像,并处理该光栅图像 .

Parsing JavaScript code

如果所需数据是用JavaScript硬编码的,则首先需要获取JavaScript代码:

  • 如果JavaScript代码在JavaScript文件中,则只需读取response.text .
  • 如果JavaScript代码在HTML页面的<script/>元素内,请使用选择器提取该<script/>元素内的文本.

一旦有了包含JavaScript代码的字符串,就可以从中提取所需的数据:

  • 您可能可以使用正则表达式以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'}
    
  • 否则,请使用js2xml将JavaScript代码转换为可以使用选择器解析的XML文档.

    例如,如果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>'
    

Pre-rendering JavaScript

在从其他请求中提取数据的网页上,重现包含所需数据的那些请求是首选方法. 付出的努力通常值得结果:结构化,完整的数据,而解析时间和网络传输时间都最少.

但是,有时候很难重现某些请求. 或者,您可能需要没有任何要求的内容,例如在网络浏览器中看到的网页的屏幕截图.

在这些情况下,请使用Splash JavaScript呈现服务以及scrapy-splash进行无缝集成.

Splash以HTML形式返回网页的DOM ,以便您可以使用selectors对其进行解析. 它通过配置脚本提供了极大的灵活性.

如果您需要Splash提供的功能之外的其他功能,例如从Python代码即时与DOM交互而不是使用以前编写的脚本,或者需要处理多个Web浏览器窗口,则可能需要使用无头浏览器 .

Using a headless browser

无头浏览器是一种特殊的Web浏览器,提供自动化API.

在Scrapy中使用无头浏览器的最简单方法是使用Selenium以及scrapy-selenium进行无缝集成.