c 抓取网页数据(0x1工具准备工欲善其事必先利其器,爬取语料的根基基于python)

优采云 发布时间: 2022-04-09 07:15

  c 抓取网页数据(0x1工具准备工欲善其事必先利其器,爬取语料的根基基于python)

  0x1 工具准备

  要想把工作做好,首先要磨砺自己的工具。爬取语料库的基础是python。

  我们基于python3开发,主要使用以下模块:requests、lxml、json。

  各模块功能简介

  01|要求

  requests 是一个 Python 第三方库,特别方便处理 URL 资源。它的官方文档有一个很大的口号:HTTP for Humans(为人类使用HTTP而生)。对比python自带的urllib体验,笔者认为requests的体验比urllib高一个数量级。

  让我们做一个简单的比较:

  网址库:

   1import urllib2

2import urllib

3

4URL_GET = "https://api.douban.com/v2/event/list"

5#构建请求参数

6params = urllib.urlencode({'loc':'108288','day_type':'weekend','type':'exhibition'})

7

8#发送请求

9response = urllib2.urlopen('?'.join([URL_GET,'%s'])%params)

10#Response Headers

11print(response.info())

12#Response Code

13print(response.getcode())

14#Response Body

15print(response.read())

复制代码

  要求:

   1import requests

2

3URL_GET = "https://api.douban.com/v2/event/list"

4#构建请求参数

5params = {'loc':'108288','day_type':'weekend','type':'exhibition'}

6

7#发送请求

8response = requests.get(URL_GET,params=params)

9#Response Headers

10print(response.headers)

11#Response Code

12print(response.status_code)

13#Response Body

14print(response.text)复制代码

  我们可以发现这两个库有一些区别:

  1. 参数的构造:urllib需要对参数进行urlencode,比较麻烦;请求不需要额外的编码,非常简洁。

  2. 请求发送:urllib需要额外将url参数构造成符合要求的形式;requests 简洁很多,直接获取对应的链接和参数。

  3. 连接方式:查看返回数据的头部信息中的“连接”。使用 urllib 库时,“connection”:“close”表示每次请求结束时关闭socket通道,requests库使用urllib3,多个请求复用一个socket,“connection”:“keep-alive”,表示多个请求使用一个连接,消耗资源较少

  4. 编码方式:requests库的编码方式Accept-Encoding比较全,这里就不举例了

  综上所述,使用requests更加简洁易懂,极大的方便了我们的开发。

  02|lxml

  BeautifulSoup 是一个库,XPath 是一种技术,python 中使用最多的 XPath 库是 lxml。

  当我们得到请求返回的页面时,我们如何得到我们想要的数据呢?此时,lxml 是一个强大的 HTML/XML 解析工具。Python从来不缺解析库,那我们为什么要在众多库中选择lxml呢?我们选择另一个知名的 HTML 解析库 BeautifulSoup 进行对比。

  让我们做一个简单的比较:

  美丽汤:

  1from bs4 import BeautifulSoup #导入库

2# 假设html是需要被解析的html

3

4#将html传入BeautifulSoup 的构造方法,得到一个文档的对象

5soup = BeautifulSoup(html,'html.parser',from_encoding='utf-8')

6#查找所有的h4标签

7links = soup.find_all("h4")

复制代码

  lxml:

  1from lxml import etree

2# 假设html是需要被解析的html

3

4#将html传入etree 的构造方法,得到一个文档的对象

5root = etree.HTML(html)

6#查找所有的h4标签

7links = root.xpath("//h4")

复制代码

  我们可以发现这两个库有一些区别:

  1.解析html:BeautifulSoup的解析方式和JQ类似。API 非常人性化,支持 css 选择器;lxml语法有一定的学习成本

  2. 性能:BeautifulSoup 是基于 DOM 的,它会加载整个文档并解析整个 DOM 树,所以时间和内存开销会大很多;而lxml只会在本地遍历,而lxml是用c写的,而BeautifulSoup是用python写的,明显的表现就是lxml>>BeautifulSoup。

  综上所述,使用 BeautifulSoup 更加简洁易用。lxml虽然有一定的学习成本,但总体来说简单易懂。最重要的是它是基于C编写的,速度要快得多。对于笔者的强迫症,自然就选择lxml了。

  03|json

  Python 自带了自己的 json 库,对于基本的 json 处理来说已经足够了。但是如果你想更懒一点,可以使用第三方的json库,常见的有demjson和simplejson。

  这两个库,无论是导入模块速度,还是编解码速度,都比simplejson好,simplejson的兼容性更好。所以如果你想使用square库,你可以使用simplejson。

  0x2 确定语料来源

  武器准备好后,下一步就是确定爬行方向。

  以电竞语料为例,现在我们来爬取电竞相关的语料。熟悉的电竞平台有企鹅电竞、企鹅电竞和企鹅电竞(斜视),所以我们使用企鹅电竞上的比赛直播作为数据源进行爬取。

  我们登陆企鹅电竞官网,进入游戏列表页面。我们可以发现页面上有很多游戏。手动写这些游戏名显然是无利可图的,于是我们开始了爬虫的第一步:游戏列表爬取。

  

   1import requests

2from lxml import etree

3

4# 更新游戏列表

5def _updateGameList():

6 # 发送HTTP请求时的HEAD信息,用于伪装为浏览器

7 heads = {

8 'Connection': 'Keep-Alive',

9 'Accept': 'text/html, application/xhtml+xml, */*',

10 'Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3',

11 'Accept-Encoding': 'gzip, deflate',

12 'User-Agent': 'Mozilla/6.1 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'

13 }

14 # 需要爬取的游戏列表页

15 url = 'https://egame.qq.com/gamelist'

16

17 # 不压缩html,最大链接时间为10妙

18 res = requests.get(url, headers=heads, verify=False, timeout=10)

19 # 为防止出错,编码utf-8

20 res.encoding = 'utf-8'

21 # 将html构建为Xpath模式

22 root = etree.HTML(res.content)

23 # 使用Xpath语法,获取游戏名

24 gameList = root.xpath("//ul[@class='livelist-mod']//li//p//text()")

25 # 输出爬到的游戏名

26 print(gameList)

复制代码

  我们拿到这几十个游戏名之后,接下来就是爬取这几十个游戏的语料库了。这时问题来了,我们要从哪个网站爬取这几十个游戏攻略,taptap?多玩?17173?分析了这些网站,发现这些网站只有一些文章热门游戏的语料库,一些冷门或者低热度的游戏,比如《灵魂芯片》、《奇迹》: Awakening”、“Death iscoming”等,在这些网站上很难找到大量的文章语料库,如图:

  

  我们可以发现,《奇迹:觉醒》和《灵魂碎片》的文章语料特别少,数量达不到我们的要求。那么有没有更通用的资源站,拥有极其丰富的文章语料库,可以满足我们的需求。

  其实静下心来想想,我们每天都在用这个资源站,那就是百度。我们在百度新闻上搜索了相关游戏,得到了搜索结果列表。这些列表的链接网页内容几乎都与搜索结果有很强的相关性,从而可以轻松解决数据源不足的问题。但是这时候又出现了一个新的问题,也是一个比较难解决的问题——如何爬取任意网页的文章的内容?

  因为不同的网站有不同的页面结构,我们无法预测会爬到哪些网站数据,也不可能为每个网站爬虫写一组数据,工作量是难以想象的!但我们不能简单粗暴地爬下页面中的所有单词。使用这样的语料库进行训练无疑是一场噩梦!

  与每个网站角力,查询资料和思考后,终于找到了一个更通用的解决方案。让我告诉你作者的想法。

  0x3 网站 的任何 文章 语料库爬取

  01|提取方法

  1)基于Dom树文本提取

  2)根据网页切分查找文本块

  3)基于标记窗口的文本提取

  4)基于数据挖掘或机器学习

  5)基于线块分布函数的文本提取

  02|萃取原理

  看到这些类型你是不是有点疑惑,它们是怎么提取出来的呢?让作者慢慢来。

  1)基于Dom树的文本提取:

  该方法主要是通过比较标准的HTML构建Dom树,然后base cabinet遍历Dom,比较识别各种非文本信息,包括广告、链接和非重要节点信息。非文字信息提取出来后,剩下的自然就是文字信息了。

  但是这种方法有两个问题

  ① 尤其依赖于HTML良好的结构。如果我们爬取一个不是按照 W3c 规范编写的网页,这种方法就不是很适合了。

  ②树的建立和遍历的时间复杂度和空间复杂度都很高,树的遍历方式也会因为HTML标签的不同而有不同的差异。

  2) 根据网页分词查找文本块:

  一种方法是在 HTML 标记中使用分隔线以及一些视觉信息(例如文本颜色、字体大小、文本信息等)。

  这种方法有一个问题:

  ①不同的网站HTML样式差别很大,没有办法统一分割,无法保证通用性。

  3) 基于标记窗口的文本提取:

  首先普及一个概念——标签窗口,我们把这两个标签和其中收录的文本组合成一个标签窗口(比如I am h1中的“I am h1”就是标签窗口的内容),取出文本的标签窗口。.

  该方法首先获取 文章 标题和 HTML 中的所有标记窗口,然后对其进行分词。然后计算标记窗口中标题序列和文本序列之间的单词距离L。如果 L 小于阈值,则标记窗口中的文本被认为是文本。

  这种方法虽然看起来不错,但实际上存在问题:

  ① 页面中的所有文字都需要切分,效率不高。

  ②词距的阈值难以确定,不同的文章阈值不同。

  4)基于数据挖掘或机器学习

  使用大数据进行训练,让机器提取正文。

  这种方法固然优秀,但是在训练之前需要html和body数据。我们不会在这里讨论它。

  5)基于线块分布函数的文本提取

  对于任何网页,它的正文和标签总是混合在一起的。该方法的核心有亮点:①文本区域的密度;②线块的长度;网页的文本区域一定是文本信息分布最密集的区域之一,而且这个区域可能是最大的(长评论信息、短文本),所以同时块长为引入判断。

  实施思路:

  ①我们先去掉HTML标签,只留下所有的文字,去掉标签后留下所有空白的位置信息,我们称之为Ctext;

  ②对每个Ctext取周围的k行(k

  ③ 去除Cblock中所有的空白字符,文本的总长度称为Clen;

  ④ 以Ctext为横坐标,每行Clen为纵坐标,建立坐标系。

  以这个网页为例:网页的文本区域从145行到182行。

  

  从上图可以看出,正确的文本区域是分布函数图上所有收录最高值且连续的区域。该区域通常收录一个膨胀点和一个坍落点。因此,网页文本提取问题转化为线块分布函数上的两个边界点,膨胀点和下降点。这两个边界点所收录的区域收录当前网页的最大行块长度,并且是连续的。.

  经过大量实验,证明该方法对中文网页的文本提取具有较高的准确率。这种算法的优点是行块功能不依赖于HTML代码,与HTML标签无关。实现简单,准确率高。

  主要逻辑代码如下:

   1# 假设content为已经拿到的html

2

3# Ctext取周围k行(k max_text_len and (not boolstart)):

38 # Cblock下面3个都不为0,认为是正文

39 if (Ctext_len[i + 1] != 0 or Ctext_len[i + 2] != 0 or Ctext_len[i + 3] != 0):

40 boolstart = True

41 start = i

42 continue

43 if (boolstart):

44 # Cblock下面3个中有0,则结束

45 if (Ctext_len[i] == 0 or Ctext_len[i + 1] == 0):

46 end = i

47 boolend = True

48 tmp = []

49

50 # 判断下面还有没有正文

51 if(boolend):

52 for ii in range(start, end + 1):

53 if(len(lines[ii]) < 5):

54 continue

55 tmp.append(lines[ii] + "n")

56 str = "".join(list(tmp))

57 # 去掉版权信息

58 if ("Copyright" in str or "版权所有" in str):

59 continue

60 main_text.append(str)

61 boolstart = boolend = False

62# 返回主内容

63result = "".join(list(main_text))

复制代码

  0x4 结语

  至此,我们可以获得任意内容的文章语料库,但这仅仅是开始。获得这些语料后,我们还需要一次清洗、分割、标记等,才能得到实际可以使用的语料。

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线