Using your browser’s Developer Tools for scraping

这是有关如何使用浏览器的开发人员工具简化抓取过程的一般指南. 如今,几乎所有浏览器都内置有开发人员工具 ,尽管在本指南中我们将使用Firefox,但这些概念适用于任何其他浏览器.

在本指南中,我们将通过引用quotes.toscrape.com来介绍浏览器开发人员工具中要使用的基本工具.

Caveats with inspecting the live browser DOM

由于Developer Tools是在实时浏览器DOM上运行的,因此检查页面源时实际上会看到的不是原始HTML,而是在应用了一些浏览器清理并执行Javascript代码之后进行的修改. 尤其是Firefox以向表添加<tbody>元素而闻名. 另一方面,Scrapy不会修改原始页面的HTML,因此,如果在XPath表达式中使用<tbody> ,则将无法提取任何数据.

因此,请记住以下几点:

  • 在检查DOM以查找要在Scrapy中使用的XPath时禁用Javascript(在"​​开发人员工具"设置中,单击" 禁用JavaScript"
  • 永远不要使用完整的XPath路径,不要使用基于属性(例如idclasswidth等)或任何contains(@href, 'image')诸如contains(@href, 'image')类的识别特征的相对巧妙的路径.
  • Never include <tbody> elements in your XPath expressions unless you really know what you’re doing

Inspecting a website

到目前为止,开发人员工具中最方便的功能是检查器功能,它使您可以检查任何网页的基础HTML代码. 为了演示检查器,让我们看一下quotes.toscrape.com -site.

在该网站上,共有来自不同作者的十个带有特定标签的报价,以及十大标签. 假设我们要提取此页面上的所有引号,而无需任何有关作者,标签等的元信息.

无需查看页面的整个源代码,我们只需右键单击报价并选择Inspect Element (Q)打开Inspector . 在其中,您应该看到类似以下的内容:

Firefox's Inspector-tool

对我们来说有趣的部分是:

<div class="quote" itemscope="" itemtype="http://schema.org/CreativeWork">
  <span class="text" itemprop="text">(...)</span>
  <span>(...)</span>
  <div class="tags">(...)</div>
</div>

如果将鼠标悬停在屏幕快照中突出显示的span标签正上方的第一个div上,则会看到网页的相应部分也被突出显示. 所以现在我们有了一个小节,但是我们在任何地方都找不到报价文本.

Inspector的优点是它可以自动扩展和折叠网页的部分和标签,从而大大提高了可读性. 您可以通过单击标签前面的箭头或直接在标签上双击来展开和折叠标签. 如果我们使用class= "text"扩展span标签,我们将看到我们单击的引用文本. 使用Inspector ,您可以将XPaths复制到选定的元素. 试试看:右键单击span标记,选择Copy > XPath并将其粘贴到scrapy shell中,如下所示:

$ scrapy shell "http://quotes.toscrape.com/"
(...)
>>> response.xpath('/html/body/div/div[2]/div[1]/div[1]/span[1]/text()').getall()
['"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”]

最后添加text() ,我们就可以使用此基本选择器提取第一引号. 但是这个XPath并不是那么聪明. 它所做的只是从html到源代码中的所需路径. 因此,让我们看一下是否可以进一步完善XPath:

如果再次检查" 检查器" ,我们将看到在扩展的div标签正下方有9个相同的div标签,每个标签都具有与第一个相同的属性. 如果我们展开其中的任何一个,我们将看到与第一个引号相同的结构:两个span标签和一个div标签. 我们可以在div标签内使用class="text"扩展每个span标签,并查看每个引号:

<div class="quote" itemscope="" itemtype="http://schema.org/CreativeWork">
  <span class="text" itemprop="text">
    “The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
  </span>
  <span>(...)</span>
  <div class="tags">(...)</div>
</div>

有了这些知识,我们就可以完善XPath:我们可以使用has-class-extension来简单地选择所有带有class="text"span标签:

 >>> response.xpath('//span[has-class("text")]/text()').getall()
['"The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”,
 '“It is our choices, Harry, that show what we truly are, far more than our abilities.”',
 '“There are only two ways to live your life. One is as though nothing is a miracle. The other is as though everything is a miracle.”',
 (...)]

通过一个简单,更聪明的XPath,我们能够提取页面中的所有引号. 我们可以在第一个XPath上构造一个循环以增加最后一个div的数量,但这将不必要地复杂,并且只需通过使用has-class("text")构造一个XPath,我们就可以提取所有引号线.

Inspector还有许多其他有用的功能,例如在源代码中搜索或直接滚动到您选择的元素. 让我们演示一个用例:

假设您要在页面上找到" Next按钮. 在" 检查器"右上方的搜索栏中键入" Next . 您应该得到两个结果. 第一个是带有class="text"li标记,第二个a标记的文本. 右键单击a标签,然后选择Scroll into View . 如果将鼠标悬停在标签上,则会看到突出显示的按钮. 从这里我们可以轻松地创建一个链接提取器来跟踪分页. 在这样的简单站点上,可能不需要视觉上查找元素,但是" Scroll into View功能在复杂站点上可能非常有用.

请注意,搜索栏还可用于搜索和测试CSS选择器. 例如,您可以搜索span.text来查找所有引用文本. 代替全文搜索,这将在页面中精确搜索带有class="text"span标签.

The Network-tool

抓取时,您可能会遇到动态网页,其中某些页面是通过多个请求动态加载的. 尽管这可能很棘手,但是开发人员工具中的Network -tool大大简化了此任务. 为了演示网络工具,让我们看一下quotes.toscrape.com/scroll页面.

该页面与基本quotes.toscrape.com -page非常相似,但是当您滚动到底部时,该页面会自动加载新的引号,而不是上述的Next按钮. 我们可以继续尝试直接尝试不同的XPath,但是相反,我们将从scrapy shell中检查另一个非常有用的命令:

$ scrapy shell "quotes.toscrape.com/scroll"
(...)
>>> view(response)

浏览器窗口应随网页打开,但有一个重要的区别:除了引号,我们看到的是带有" Loading... "字样的绿色栏.

Response from quotes.toscrape.com/scroll

通过view(response)命令,我们可以查看shell或后来的Spider从服务器接收到的响应. 在这里,我们看到已加载了一些基本模板,其中包括标题,登录按钮和页脚,但引号缺失了. 这告诉我们报价是从不同于quotes.toscrape/scroll请求中加载的.

如果单击" Network选项卡,则可能只会看到两个条目. 我们要做的第一件事是通过单击Persist Logs启用持久Persist Logs . 如果禁用此选项,则每次导航到其他页面时都会自动清除日志. 启用此选项是一个很好的默认设置,因为它使我们可以控制何时清除日志.

如果我们现在重新加载页面,您将看到日志中填充了六个新请求.

Network tab with persistent logs and requests

在这里,我们看到重新加载页面时已发出的每个请求,并且可以检查每个请求及其响应. 因此,让我们找出我们的报价来自哪里:

首先单击名称为scroll的请求. 现在,您可以在右侧检查请求. 在Headers ,你会发现关于请求头,如URL,该方法中,IP地址等详细信息. 我们将忽略其他选项卡,然后直接单击Response .

您应该在" Preview窗格中看到的是呈现的HTML代码,这正是我们在shell中调用view(response)时所看到的. 因此,日志中的请求typehtml . 其他请求的类型类似于cssjs ,但我们感兴趣的是一个名为quotes?page=1且类型为json请求.

如果单击此请求,则会看到请求URL为http://quotes.toscrape.com/api/quotes?page=1 ,响应是包含引号的JSON对象. 我们还可以右键单击该请求,然后Open in new tab以获得更好的概述.

JSON-object returned from the quotes.toscrape API

With this response we can now easily parse the JSON-object and also request each page to get every quote on the site:

import scrapy
import json


class QuoteSpider(scrapy.Spider):
    name = 'quote'
    allowed_domains = ['quotes.toscrape.com']
    page = 1
    start_urls = ['http://quotes.toscrape.com/api/quotes?page=1']

    def parse(self, response):
        data = json.loads(response.text)
        for quote in data["quotes"]:
            yield {"quote": quote["text"]}
        if data["has_next"]:
            self.page += 1
            url = "http://quotes.toscrape.com/api/quotes?page={}".format(self.page)
            yield scrapy.Request(url=url, callback=self.parse)

这个蜘蛛从引号API的第一页开始. 对于每个响应,我们都会解析response.text并将其分配给data . 这使我们可以像处理Python字典一样对JSON对象进行操作. 我们遍历quotes并打印出quote["text"] . 如果方便的has_next元素为true (尝试在浏览器中加载quotes.toscrape.com/api/quotes?page=10或页面号大于10),我们将增加page属性并yield一个新请求,并插入增加的进入我们url页面编号.

在更复杂的网站中,可能很难轻松地重现请求,因为我们可能需要添加headerscookies才能使其正常工作. 在这种情况下,可以通过在网络工具中右键单击每个请求并使用from_curl()方法生成等效请求,以cURL格式导出请求:

from scrapy import Request

request = Request.from_curl(
    "curl 'http://quotes.toscrape.com/api/quotes?page=1' -H 'User-Agent: Mozil"
    "la/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0' -H 'Acce"
    "pt: */*' -H 'Accept-Language: ca,en-US;q=0.7,en;q=0.3' --compressed -H 'X"
    "-Requested-With: XMLHttpRequest' -H 'Proxy-Authorization: Basic QFRLLTAzM"
    "zEwZTAxLTk5MWUtNDFiNC1iZWRmLTJjNGI4M2ZiNDBmNDpAVEstMDMzMTBlMDEtOTkxZS00MW"
    "I0LWJlZGYtMmM0YjgzZmI0MGY0' -H 'Connection: keep-alive' -H 'Referer: http"
    "://quotes.toscrape.com/scroll' -H 'Cache-Control: max-age=0'")

另外,如果您想知道重新创建该请求所需的参数,则可以使用scrapy.utils.curl.curl_to_request_kwargs()函数来获取包含等效参数的字典.

如您所见,通过对Network -tool的一些检查,我们能够轻松地复制页面滚动功能的动态请求. 搜寻动态页面可能非常艰巨,页面可能非常复杂,但是(主要是)归结为识别正确的请求并将其复制到蜘蛛中.