Python网络数据采集入门教程

Python网络数据采集入门教程

 

在这篇文章中,我们将介绍Python提供的几乎所有的网络数据采集工具,你可以将本文看作是我们的《终极网络数据采集指南》的系列文章。我们将从最基本的工具到最先进的工具进行介绍,并将涵盖每一个的利弊。当然,我们并不能涵盖我们讨论的每个工具的所有方面,但是这篇文章应该足以让你了解哪些工具可以做什么,以及何时使用哪些工具。

注意:当我在这篇博客文章中谈论Python时,你应该假设我谈论的是Python3。

Python网络数据采集入门教程

0)网络基础知识

互联网是非常复杂的: 在浏览器中查看一个简单的网页涉及到许多底层技术和概念。我并不打算解释所有的东西,但是为了从网络中提取数据,我将向你展示一些你必须理解的最重要的东西。

超文本传输协议(HTTP)

HTTP使用一个客户机/服务器模型,其中一个HTTP客户机(浏览器、你的Python程序、curl和Requests等)打开一个连接并向一个HTTP服务器(Nginx和Apache等)发送一条消息(“我想看那个页面:/product”)。

然后,该服务器使用一个响应(例如HTML代码)进行回答并关闭连接。HTTP被称为无状态协议,因为每个事务(请求/响应)都是独立的。例如,FTP是有状态协议。

基本上,当你在浏览器中输入一个网址时,HTTP请求看起来是这样的:

Python网络数据采集入门教程

 

在这个请求的第一行,你可以看到很多东西:

  • 这里使用了GET 动词或方法,这表示我们是从特定的路径(: /product/)请求数据的。还有其他的HTTP动词,你可以在这里看到完整的列表。
  • HTTP协议的版本,在本教程中我们将专注HTTP 1.
  • 多个标头字段

以下是最重要的标头字段:

  • Host: 服务器的域名,如果没有指定端口号,则默认为80。
  • User-Agent: 包含客户端发起的请求的信息,包括操作系统信息。在本例中,它是我的OSX系统上的网络浏览器(Chrome)。这个标头很重要,因为它要被用于统计(有多少用户访问了我的手机网站或桌面网站)或用于防止任何来自机器人的违规行为。因为这些标头是由客户端发送的,所以可以对它进行修改(称为“标头欺骗”),这正是我们使用scraper所要做的——使scraper看起来像一个普通的网络浏览器。
  • Accept: 可接受的作为响应的内容类型。有很多不同的内容类型和子类型:text/plain、text/html、image/jpeg和application/json等。
  • Cookie : name1=value1;name2=value2... ,此标头字段包含一个名称-值对列表。它被称为会话cookies,用于存储数据。Cookies是网站用来验证用户身份, 和/或在你的浏览器中存储数据的工具。例如,当你填写了一个登录表单时,服务器将检查你输入的凭据是否正确,如果正确,它将进行重定向并将一个会话cookie注入你的浏览器。然后,你的浏览器会将此cookie与随后的每个请求一起发送到该服务器。
  • Referrer: Referrer标头包含跳转到实际URL页面的源URL地址。这个标头很重要,因为网站使用这个标头来根据用户来源来改变自身的行为。例如,许多新闻网站都有付费订阅,你只能浏览文章的10%,但如果用户来自像Reddit这样的新闻聚合器,网站就会让你浏览全部内容。它们使用referrer来检查这个。有时,我们将不得不欺骗这个标头来获得我们想要提取的内容。

标头列表中还有很多字段,你可以在这里找到完整的标头列表:https://en.wikipedia.org/wiki/List_of_HTTP_header_fields。

服务器会像这样进行响应:

Python网络数据采集入门教程

 

在第一行,我们有一个新的信息,HTTP代码 200 OK。这意味着此请求已经成功。对于请求标头来说,有很多HTTP代码,分为四个常见类:2XX表示请求成功、3XX表示重定向、4XX表示请求异常(最出名的是404 Not found)和5XX表示服务器错误。

然后,如果你使用网络浏览器发送此HTTP请求,浏览器将解析HTML代码,获取所有最终前端资源(Javascript文件、CSS文件、图像等),并将结果呈现到主窗口。

在接下来的部分中,我们将看一些使用Python执行HTTP请求,并从响应中提取我们想要的数据的不同方法。

1)手动打开一个socket(套接字)并发送HTTP请求

Socket

在Python中执行HTTP请求的最基本方法是打开一个socket并手动发送HTTP请求。

Python网络数据采集入门教程

 

现在我们有了HTTP响应,从它里面提取数据的最基本方法是使用正则表达式。

正则表达式

正则表达式(RE或Regex)是字符串的搜索模式。使用regex,你可以在一个更大的文本体中搜索特定的字符/单词。

例如,你可以识别网络页面中的所有电话号码。你还可以替换某些项,例如,你可以将HTML中格式糟糕的所有大写标记替换为小写标记。你还可以校验一些输入…

正则表达式使用的模式是从左到右应用的。每个源字符只使用一次。你可能想知道为什么在进行网络数据挖掘时了解正则表达式很重要?

总之,有各种不同的Python模块可以来解析HTML,你可以使用XPath和CSS选择器。

在一个理想的语义世界中,数据是易于机器读取,信息被嵌入到相关的HTML元素中,并带有有意义的属性。

但是现实世界是混乱的,你经常会在一个p元素中发现大量的文本。当你想在这个巨大的文本中提取一个特定的数据时,例如价格、日期或名称等,你必须使用正则表达式。

注意:这里有一个很棒的网站来测试你的正则表达式: https://regex101.com/ ,和一个很棒的博客(https://www.rexegg.com/ )来了解更多关于它们的信息,这篇文章将只涵盖你可以使用regexp做的事情的一小部分。

当你有这类数据时,正则表达式可以很有用:

Python网络数据采集入门教程

 

我们可以使用一个Xpath表达式来选择这个文本节点,然后使用这种正则表达式来提取价格:

Python网络数据采集入门教程

 

要提取一个HTML标记内的文本,使用正则表达式很烦人,但却是可行的:

Python网络数据采集入门教程

 

正如你所看到的,你可以使用一个socket手动发送HTTP请求,并使用正则表达式解析响应,但是这很复杂,而且有更高级的API可以使这项任务更容易。

2) urllib3 和 LXML

免责声明: 在Python的urllib中很容易迷失方向。urllib和urllib2是标准库的一部分,你还可以找到urllib3。urllib2在python3中被分成多个模块,而且urllib3可能随时就不会成为标准库的一部分。这整个令人困惑的东西将成为一篇博客文章的主题。在本部分中,我选择只讨论urllib3,因为它在Python世界中被广泛使用,只举两个例子,比如被pip和requests库使用。

Urllib3是一个高级包,它允许你对一个HTTP请求做几乎任何你想做的事情。它允许我们用更少的代码行来做上面用socket所做的事情。

Python网络数据采集入门教程

 

这比socket版本更简洁。不仅如此,该API非常简单易懂,你可以轻松地做许多事情,比如添加HTTP标头、使用代理、POST表单等。

例如,如果我们决定设置一些标头并使用一个代理,我们只需要这样做。

Python网络数据采集入门教程

 

看到了吗?完全相同的代码行数,但是,有一些事情urllib3处理起来并不容易,例如,如果我们想添加一个cookie,我们必须手动创建相应的标头并将其添加到请求中。

还有一些事情urllib3可以做,但requests做不了。有的事情是requests不能做的,例如池和代理池的创建和管理、重试策略的控制。

简单地说,urllib3在抽象方面介于requests和socket之间,尽管它比socket更接近requests。

这一次,为了解析响应,我们将使用lxml包和XPath表达式。

XPath

Xpath是一种使用路径表达式来选择XML文档(或HTML文档)中的节点或节点集的技术。与文档对象模型一样,Xpath自1999年以来一直是一个W3C标准。即使Xpath本身不是一种编程语言,但它也允许你编写可以直接访问一个特定节点或特定节点集的表达式,而无需遍历整个HTML树(或XML树)。

你可以将XPath看作regexp,但主要是针对XML/HMTL。

要用XPath从一个HTML文档中提取数据,我们需要三个东西:

  • 一个HTML 文档
  • 一些XPath 表达式
  • 一个可以运行这些表达式的XPath 引擎

首先,我们将使用由于urllib3所得到的HTML,我们只想从谷歌主页中提取所有链接,因此我们将使用一个简单的XPath表达式://a,并使用LXML来运行它。LXML是一个支持XPATH的快速且易于使用的XML和HTML处理库。

安装:

Python网络数据采集入门教程

 

下面是前一段代码之后的代码:

Python网络数据采集入门教程

 

输出应该是这样的:

Python网络数据采集入门教程

 

你必须记住,这个示例非常非常简单,并没有真正向你展示XPath可以有多么强大(注意:这个XPath表达式应该改为 //a/@href,以避免必须对links进行迭代才能获得它们的href)。

如果你想了解更多关于XPath的知识,你可以阅读这篇很好的介绍(https://librarycarpentry.org/lc-webscraping/02-xpath/index.html )。LXML文档也编写得很好,是一个很好的起点(https://lxml.de/tutorial.html )。

XPath表达式(像regexp一样)非常强大,是从HTML中提取信息的最快方法之一,而且与regexp一样,XPath很快就会变得混乱、难于阅读和维护。

3) requests 和 BeautifulSoup

Python网络数据采集入门教程

 

Requests是python包中的王者,拥有超过1100万的下载量,是Python中使用最广泛的包。

安装:

Python网络数据采集入门教程

 

使用Requests包发出一个请求(没有评论)真的很简单:

Python网络数据采集入门教程

 

使用Requests可以很容易地执行POST请求,处理cookie,查询参数…

Hacker News认证

假设我们想要创建一个工具来自动向Hacker news或任何其他论坛(比如Buffer)提交我们的博客文章。在发布我们的链接之前,我们需要对这些网站进行认证。这就是我们要使用Requests 和 BeautifulSoup所做的事情!

下面是Hacker News的登录表单和相关的DOM:

Python网络数据采集入门教程

 

这个表单上有三个 <input>标记,第一个标记有一个隐藏类型和一个“goto”名称,另外两个标记是用户名和密码。

如果你在你的Chrome浏览器中提交此表单,你会看到有很多事情正在发生:一个重定向和一个cookie正在被设置。这个cookie将由Chrome在随后的每个请求中进行发送,以便服务器知道你是经过身份验证的。

使用Requests做这些很容易,它将自动为我们处理重定向,并且可以使用Session对象处理cookies。

接下来我们需要的是BeautifulSoup,它是一个Python库,可以帮助我们解析服务器返回的HTML,以确定我们是否登录了。

安装:

Python网络数据采集入门教程

 

因此,我们所要做的就是将这三个输入连同我们的凭证一起POST到/login端点,并检查是否存在一个只在登录后才显示的元素:

Python网络数据采集入门教程

 

为了了解更多关于BeautifulSoup的信息,我们可以尝试提取主页上的每个链接。

顺便说一下,Hacker News提供了一个功能强大的API,所以我们以它为例,但是你应该使用这个API而不是进行抓取!

我们需要做的第一件事是检查Hacker New的主页,以了解我们将必须选择的结构和不同的CSS类:

我们可以看到所有的文章都在一个<tr class="athing"> 中,所以我们需要做的第一件事就是选择所有这些标记。这使用以下代码可以很容易做到:

Python网络数据采集入门教程

 

然后对于每个链接,我们将提取它的id, title, url和rank:

Python网络数据采集入门教程

 

正如你所看到的,Requests和BeautifulSoup是通过发布表单来提取数据和自动化不同内容的优秀库。如果你想做大规模的网络数据挖据项目,你仍然可以使用Requests,但是你需要自己处理很多事情。

当你需要抓取很多网页时,有很多事情你必须处理:

  • 找到一种并行化代码的方法使其更快
  • 处理错误
  • 存储结果
  • 过滤结果
  • 终止你的请求避免服务器过载

幸运的是,有一些工具可以为我们处理这些事情。

4) Scrapy

Python网络数据采集入门教程

 

Scrapy是一个功能强大的Python 数据挖掘框架。它提供了许多功能来异步下载网络页面、处理和保存它。它能处理多线程、爬行(从一个链接到另一个链接以找到网站中的每个URL的过程)、站点地图爬行等等。

Scrapy还有一种交互模式,称为Scrapy Shell。使用Scrapy Shell,你可以非常快速地测试你的抓取代码,比如XPath表达式或CSS选择器。

Scrapy的缺点是它的学习曲线陡峭,需要学习的东西很多。

为了继续我们关于Hacker news的示例,我们将编写一个Scrapy Spider,它将抓取前15页的结果,并将所有内容保存在一个CSV文件中。

你可以使用pip轻松安装Scrapy:

Python网络数据采集入门教程

 

然后你可以使用scrapy 命令行为我们的项目生成样板代码:

Python网络数据采集入门教程

 

在hacker_news_scraper/spider中,我们将使用我们的爬虫的代码创建一个新的python文件:

Python网络数据采集入门教程

 

在Scrapy中有很多约定,这里我们定义了一个起始url数组。我们将通过Scrapy命令行使用属性名来调用我们的Spider。

我们将在start_urls数组中的每个URL上调用解析方法:

然后,我们需要稍微调整一下Scrapy,以便我们的爬虫在目标网站上表现良好。

Python网络数据采集入门教程

 

你应该始终打开这个选项,它将通过分析响应时间和调整并发线程的数量来确保目标网站不会被你的爬虫拖慢。

你可以使用Scrapy CLI并以不同的输出格式(CSV, JSON, XML…)来运行这段代码:

Python网络数据采集入门教程

 

就是它!现在,你的所有链接都保存在一个格式良好的JSON文件中。

5) Selenium 和 Chrome—无UI模式

对于大规模的网络抓取任务来说,Scrapy确实很好,但是如果你需要抓取用Javascript框架编写的单页面应用程序,那么它就没那么好了,因为它无法呈现Javascript代码。

抓取这些SPA可能很有挑战性,因为它们通常涉及许多AJAX调用和websockets连接。如果性能是一个问题,你应该总是会尝试重新生成Javascript代码,这意味着你要使用浏览器检查器手动检查所有网络调用,并复制包含有趣数据的AJAX调用。

在某些情况下,要获得所需的数据涉及到太多的异步HTTP调用,而在无UI浏览器中呈现页面可能更容易一些。

另一个很好的用例是对一个页面进行截屏,这就是我们要对Hacker News主页做的(再次!)

你可以使用pip安装selenium包:

Python网络数据采集入门教程

 

你还需要Chromedriver:

Python网络数据采集入门教程

 

然后我们只需从selenium包中导入Webdriver,用headless=True配置Chrome,并设置窗口大小(不然截图会非常小):

Python网络数据采集入门教程

 

你应该会得到一个很好的主页截图:

你可以使用Selenium API和Chrome做更多的事情,比如:

  • 执行 Javascript
  • 填写表单
  • 在元素上进行点击
  • 使用CSS选择器/ XPath表达式提取元素

Selenium和无UI模式下的Chrome是抓取你想要的任何信息的终极组合。你可以自动化任何你使用普通的Chrome浏览器所做的事情。

最大的缺点是Chrome需要大量的内存/ CPU性能。通过一些微调,你可以将每个Chrome实例的内存占用减少到300-400mb,但每个实例仍然需要一个CPU核心。

如果你想同时运行几个Chrome实例,你需要强大的服务器(成本会迅速上升)和持续的资源监控。

结论

下面是我们在本文中讨论的每种技术的一个快速回顾表。如果你知道一些与本文相关的资源,请在评论中告诉我们。

Python网络数据采集入门教程

 

我希望本文的概述能够帮助你有效地选择Python数据挖掘工具,并希望你在阅读本文时能够学到一些东西。

我在这篇文章中讨论的所有内容都是我用来构建ScrapingNinja的东西,也就是这里提到的最简单的网络数据挖掘API。如果你不想浪费太多的时间设置一切的话,就赶紧去测试我们的解决方案吧,第一个1k 次API调用是免费的!

我在这篇文章中提到的每一个工具都将是我未来的一个特定博文的主题,我将在其中进行详细讨论。

不要犹豫,请及时在评论中告诉我你想知道关于数据挖掘的什么知识,我将在下一篇文章中讨论它。

爬爬更健康!~

想得到更多的技术文章推送可以关注我的个人公众号:

Python网络数据采集入门教程