简单页面的爬取 |
- 准备 Request 库和 User Agent
在日常工作中我们有时候需要一个简单的程序代替人工进行大规模的阅读去爬取有用的信息,也就是我们常说的“爬虫”程序。
首先我们需要安装一个简单的Requests库(当然你也可以安装其它的库进行爬虫)
pip install requests
Request 库是一个常用的 http 请求库,它本身是用 Python 编写的。Request 基于 urllib,但是比 urllib 的易用性要好很多。而且语法简单一点,功能一点也不弱。
在 Request 库安装完成之后,要对目标网站进行请求之前还需要搞清楚自己的 user agent。
User agent 一般翻译为 “ 用户代理 ”,它的作用是向服务器 “ 自报家门 ”,告诉服务器我们的电脑操作系统、CPU、浏览器、以及浏览器的版本信息。这样便可以让爬虫假装是一个正常用户在使用浏览器对目标的服务器发出请求,不然很容易被识破身份。对于有些网站用户的 user agent 进行校验,如果没有的话,就会被服务器据之门外。
想要知道自己的 user agent 直接浏览器中搜索 “ UA 查询 ”:
然后随便点开一个,这里点击的是第一个链接,得到下图:
可以看到使用的浏览器是 Firefox,版本是 68.0。渲染引擎是 Gecko 68.0,操作系统是 ubuntu
Mozilla/5.0(X11; Ubuntu; Linux x86_64; rv:68.0)Gecko/20100101 Firefox/68.0
- 确定一个目标网站并分析其结构
确定一个网站,例如 www.toutiao.com,如图所示:
接着选择科技专栏,这一栏地址是 http://www.toutiao.com/ch/news_tech/ :
点击一条信息如锦鲤女孩一夜暴富,并记录地址 https://www.toutiao.com/a6706027965865541896/。
- 进行爬取并保存为本地文件
接下来使用锦鲤女孩做个实验:
程序显示导入了 requests 库,并使用了之前查询到的 User Agent 来制定爬虫的 headers。接下来用 requests 向服务器发送请求:
程序中使用了刚才锦鲤女孩新闻的那个链接,headers 参数就传入 user agent。可以看到传入的 html 文件。下面查看一下 requests 的编码方式:
可以看到默认编码方式为 utf-8。有些时候可能是其它方式的编码,在之前的图中找到运行结果中的 charset 的部分:
从图中可以看到这个新闻的正文页面采用的是 “ utf-8 ” 的编码方式。
理论上,requests 库可以根据页面头猜测页面的编码方式,但它的猜测的正确率并不高,虽然这里猜测正确了。若是猜测错误,可以通过如下方式进行调整:
# 修改 encoding 为 utf-8
r.encoding = 'utf-8'
# 重新打印结果
print(r.text)
得到正确的编码方式之后,为了让页面更加清晰易读,可以采用以下两种方式:一是将这个页面保存为 html 文件,这样就可以用浏览器打开,从而进行清晰地阅读其中的内容;另一种方法是使用 html 解析器,将页面中重要的内容抽取出来,保存为所需要的任意格式的文件(如 CSV)。
下面用第一个方法实现文件的保存:
运行代码后,可得到如下结果:
这里只是简单的展示了 requests 的基本用法,如果这样将整个页面复制下来并没有什么意义,然而降低了阅读效率。
稍微复杂一点的爬取 |
如果单纯爬取一个页面并保存,并不会提高我们的效率。实际中我们所期望的结果应该如下图中所示,查看各个专栏有没有我们所需的标题和链接。
- 确定目标页面并进行分析
在网页页面上单击鼠标右键,再弹出的菜单中选择 “ 检查 ” 这一项可得下图:
图中蓝色底纹选中的就是锦鲤女孩对应的元素,而它存储于下面图中蓝色底纹的元素中,此处已经点击 <ul>标签展开了这个元素。
从中可以看到,展开<ul>后,它的下一级是多个<li>元素,继续展开第一个<li>元素后可以得到下图:
接下来,就是要使用 HTML 解析器来获取这些内容,但是现在出现了一个新的问题,我们需要让 HTML 解析器只拽去标题的文本和对应的链接,而不希望有多余的内容被提取出来。所以 “ 正则表达式 ” 就闪亮登场了。
- Python 中的正则表达式
正则表达式是一个特殊的字符序列,它能帮助我们方便地检查一个字符串是否与某种模式匹配。
Python 中,有一个称为 re 的模块能够提供全部的正则表达式功能,下面实例介绍一下。
例 1:
上面程序中,我们先导入了 re 模块,指定 re 的匹配模式为:\d+,意思是匹配一个或多个数字,这里 “ \d+ ” 被称为元字符,如果我们不添加 “ + ” 的话,那就只会匹配 1 个数字。
“ \d+ ” 前面的 “ r ” 意思是不要对 “ \ ” 进行转义——我们知道,在 Python 中,“ \ ” 表示转义符,如我们常用的 “ \n ” 就表示换行。如果不希望 Python 对 “ \ ” 进行转义,有两种方法;一是在转义符前面再增加一个斜杠 “ \ ” ,如 “ \n ” ,那么 Python 就不会对字符进行转义;另一种方法就是再前面添加 “ r ”,如本例中的 “ r ’ \d+ ’ ”。
从结果可以看出,指定的匹配模式是从开头匹配数字,第一句不是数字开头,程序打印了 “ 匹配失败 ”。而第二句是数字开头,所以匹配成功,而后使用 .group() 可以获得匹配的内容,因此将数字打印了出来。
例 2:
这个例子中,数字移动到中间的位置,然后使用.search() 来进行搜索。从结果看出来,数字依然可以被搜索出来。
此外,re 模块还提供了多种语法可以实现不同的功能,如例 3:
这里我们使用了 .split() 方法将数字之间的文本拆分出来,结果显示把整句话的文字都拆分出来了。
此外,还可以用 .findall() 语法把数字全部提取出来,如下例 4:
可以看到,把整句话的数字提取出来了。使用正则表达式不仅仅可以匹配数字,还可以使用其他的元字符来匹配各种各样的内容。下表是正则化中比较常用的元字符列表:
实例 | 描述 |
---|---|
- | 匹配除 “ \n ” 之外的任何单个字符,要匹配包括 “ \n ” 在内的任何字符,请使用象 ’ [.\n] ’ 的模式。 |
\d | 匹配一个数字字符,等价于 [0-9] |
\D | 匹配一个非数字字符,等价于[^0-9] |
\s | 匹配任何空白字符,包括空格、字符表、换页符等等,等价于 [\f\n\r\t\v] |
\S | 匹配任何非空白字符。等价于 [^\f\n\r\t\v] |
\w | 匹配包括下划线的任何单词字符。等价于 ‘[A-Za-z0-9]’ |
\W | 匹配任何非单词字符。等价于 ‘[^A-Za-z0-9]’ |
- 使用 BeautifulSoup 进行 HTML 解析
在 Python 中,有两个常用的用于 HTML 解析的库,分别是 “ lxml ” 和 “ BeautifulSoup ”,它们都可以从 HTML 文件或者 XML 文件中提取给类型的数据。二者的功能没有太大的区别,下面用 BeautifulSoup 进行介绍。首先安装相应的软件包:
# 安装 BeautifulSoup4 包
pip intsall beautifulsoup4
# 安装 lxml 包
pip install lxml
接下来用 requests 库爬取的页面进行来介绍 BeautifulSoup 的使用方法。
可以看到,先导入了 BeautifulSoup,然后创建一个名为 soup 的对象,这里指定了 BeautifulSoup 使用 lxml 作为 HTML 解析器(也可以不使用 lxml,也可以用 Python 标准库中的 HTML 解析器。不过实际应用中,lxml 解析的速度会比 Python 标准库快一些)。
结果中可以看出,文件包含了若干个标签 (Tag),每个标签都注明了其作用。例如,<head>标签注明出这部分是 HTML 文件的头部,而 <title> 标签表明这部分是文件的标题,每个标签以反斜杠 “ / ” 结束,如 </title>表示标题部分结束。
下面例子中,使用 BeautifulSoup 将 title 进行提取:
可以看到 BeautifulSoup 将页面标题进行了提取,但是提取的内容还带着标签 <title> 和 </title>,如果只要结果中只有中间的文字,而不是显示标签的内容,可以使用以下两种方法来提取。
一、使用 .string 来提取文字部分:
二、使用 .get_text() 来提取文字部分:
可以发现结果是完全一样的,所以使用的时候两种方式可以互换。
从上面可以看出,使用 BeautifulSoup 进行 HTML 文件的数据提取是非常容易的,下面尝试着提取正文的部分,首先得查看一下对象 soup,看一下正文保存在那个标签中。
代码读取显示有问题,可以试试查找其它的标签
程序中使用了 soup.div.string 来提取 <div> 标签的正文内容,可以发现这个标签正文中没有字符串形式的内容。而且默认情况下只对第一个 <div> 标签的内容进行提取,下面使用 BeautifulSoup 中的 find_all 来找到所有 <div> 标签的内容并进行提取。
可以看到依旧没有内容。原因是这里所爬取的网页中,没有选择合适的标签,大家可以试试其它网页,然后查找有文字的标签进行提取。
对文字的提取的基本技能如上,如果要对其中的链接提取,可以回到 soup 对象中然后找到包含链接的部分进行试验,这里以 <div> 标签的正文内容为示范来提取链接(结果是没有链接的,因为前面已经看到正文没有内容):
上面使用了 BeautifulSoup 中的 find_all() 来寻找所有的 <div> 标签,然后用 [-1] 来把最后一个标签赋值给变量 link,接下来使用了 .get(’ herf ') 语句将标签中的链接进行提取。
- 对目标页面进行爬取并保存到本地
在上向 https://www.toutiao.com/ch/news_tech/ 发出请求时,我们可以发现在 soup 中爬取的链接中有 media 这个单词,这个给了我们带来了很大的便利,只要使用正则表达式匹配 media 就可以获得相应的标题和链接。
下面继续查看一下 csv 文件的内容,如下:
可以看出,相应的标题和链接已经存取下来。在实际中可以根据自己感兴趣的关键词进行搜索。
对文本数据进行话题提取 |
前面介绍了如何使用 Python 的 Requests 库和 BeautifulSoup 库实现一个简单的爬虫程序,把网页上的内容爬取下来并保存为本地文件。而如果程序爬取的文本特别多的时候,即使保存在本地,也要花费长时间去读取。如果希望快速了解一大段文字的核心内容,可以使用 “ 潜在狄利克雷分布 ” (Latent Dirichlet Allocation)来对文本进行话题提取。
- 寻找目标网站并分析结构
下面在 “ 百思不得姐 ” 网页中练习潜在狄利克雷分布进行话题提取。可以看到地址栏显示为 http://www.budejie.com/text/ ,说明文字的段子页面是以 text 为后缀的,继续分析这个页面会发现每个页面包含 20 个文字段子,单击下一页会发现地址栏的内容变为 http://www.budejie.com/text/2。
可以看到,该网站是在网址最后加上数字来区分页面,这样我们就可以使用 for 循环来爬取所有页面的信息,需要注意的是,这个网站只显示 50 页最新的段子,翻到第 51 页的时候,会提示找不到页面:
这样的话,我们把目标页面锁定在 1 ~ 50,接下来检查一下页面的结构,子网页上单击鼠标右键,选择 “ 检查 ” 选项:
可以看到段子的正文保存在
- 编写爬虫进行内容爬取
直接给出程序:
接着将爬取到的内容存入 txt 文档中。
这样文件夹中就多了一个 jokes.txt 的文件。
- 使用潜在狄利克雷分布进行话题提取
潜在狄利克雷分布 (Latent Dirichlet Allocation,LDA)是基于不同的词语共同出现的频率来进行分组的模型,比如在某个文档中,“ 妹子 ” 和 “ 吃货 ” 这两个词经常同时出现,那么 LDA 便会将这个两个词归入同一个话题(Topic)中。对于 LDA 模型来说,文本必须是一些由话题组合成的集合,但需要注意的是,对于机器来说,“ 话题 ” 这个词的含义和日常所理解的是完全不同的概念,机器所理解的 “ 话题 ”,并非语义学上所指的话题,而只是通过对文本进行特征提取后所进行的聚类(clustering)。下面开始使用潜在狄利克雷分布对爬取的段子进行话题分类,在不同的话题中,哪些词共同出现的频率最高。
首先,需要载入之前保存的 txt 文件,并且把文本数据提取出来:
在用结巴分词工具,对段子的文本进行分词处理:
之后会发现,文件夹中多了一个 cutjokes.txt 的文件。
接下来,需要用这个进行过分词处理的文件来进行话题提取的工作,首先需要将文本数据转化为向量,运用 CountVestorizer 或者是 TfidfVectorizer,下面用 TfidfVectorizer,然后使用 LDA 模型进行话题提取:
从结果中可以看出,我们将每个话题中共同出现频率最高的 20 个词语给统计了出来。
网页爬取补充 |
之前主要是使用别人整理好的数据集来进行机器学习算法模型训练,但是在实际情况中,很难找到非常合适的数据集。针对这个问题题,在这部分使用了 Python 进行数据爬取的方法,以及使用潜在狄利克雷分布对文本数据进行话题提取的方法。这里使用的是简单的爬虫,如果希望进行更加复杂的爬虫,可以使用 Python 的另外一个库,称为 Scrapy,这也是目前最常用的用于开发爬虫的工具之一。
话题提取也是自然语言处理中的一小部分,目前非常流行的研究方向是——使用循环神经网络(RNNs)来进行文本的处理。
小编机器学习学的一般,只是日常做做比记加深一下印象,望读者不吝赐教,谢谢!