python读取html

时间: 2023-09-30 admin IT培训

python读取html

python读取html

# 更新游戏列表def_updateGameList:# 发送HTTP请求时的HEAD信息,用于伪装为浏览器heads = {'Connection': 'Keep-Alive','Accept': 'text/html, application/xhtml+xml, */*','Accept-Language': 'en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3','Accept-Encoding': 'gzip, deflate','User-Agent': 'Mozilla/6.1 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko'}# 需要爬取的游戏列表页url = ''

# 不压缩html,最大链接时间为10妙res = requests.get(url, headers=heads, verify= False, timeout= 10)# 为防止出错,编码utf-8res.encoding = 'utf-8'# 将html构建为Xpath模式root = etree.HTML(res.content)# 使用Xpath语法,获取游戏名gameList = root.xpath( "//ul[@class='livelist-mod']//li//p//text")# 输出爬到的游戏名print(gameList)

当我们拿到这几十个游戏名后,下一步就是对这几十款游戏进行语料爬取,这时候问题就来了,我们要从哪个网站来爬这几十个游戏的攻略呢,taptap?多玩?17173?在对这几个网站进行分析后,发现这些网站仅有一些热门游戏的文章语料,一些冷门或者低热度的游戏,例如“灵魂筹码”、“奇迹:觉醒”、“死神来了”等,很难在这些网站上找到大量文章语料,如图所示:

我们可以发现,“ 奇迹:觉醒”、“灵魂筹码”的文章语料特别少,数量上不符合我们的要求。那么有没有一个比较通用的资源站,它拥有着无比丰富的文章语料,可以满足我们的需求。

其实静下心来想想,这个资源站我们天天都有用到,那就是百度。我们在百度新闻搜索相关游戏,拿到搜索结果列表,这些列表的链接的网页内容几乎都与搜索结果强相关,这样我们数据源不够丰富的问题便轻松解决了。但是此时出现了一个新的问题,并且是一个比较难解决的问题——如何抓取到任意网页的文章内容?

因为不同的网站都有不同的页面结构,我们无法与预知将会爬到哪个网站的数据,并且我们也不可能针对每一个网站都去写一套爬虫,那样的工作量简直难以想象!但是我们也不能简单粗暴的将页面中的所有文字都爬下来,用那样的语料来进行训练无疑是噩梦!

经过与各个网站斗智斗勇、查询资料与思索之后,终于找到一条比较通用的方案,下面为大家讲一讲笔者的思路。

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

01|提取方法

1)基于Dom树正文提取

2)基于网页分割找正文块

3)基于标记窗的正文提取

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

5)基于行块分布函数正文提取

02|提取原理

大家看到这几种是不是都有点疑惑了,它们到底是怎么提取的呢?让笔者慢慢道来。

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

这一种方法主要是通过比较规范的HTML建立Dom树,然后地柜遍历Dom,比较并识别各种非正文信息,包括广告、链接和非重要节点信息,将非正文信息抽离之后,余下来的自然就是正文信息。

但是这种方法有两个问题

① 特别依赖于HTML的良好结构,如果我们爬取到一个不按W3c规范的编写的网页时,这种方法便不是很适用。

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

2) 基于网页分割找正文块 :

这一种方法是利用HTML标签中的分割线以及一些视觉信息(如文字颜色、字体大小、文字信息等)。

这种方法存在一个问题:

① 不同的网站HTML风格迥异,分割没有办法统一,无法保证通用性。

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

先科普一个概念——标记窗,我们将两个标签以及其内部包含的文本合在一起成为一个标记窗(比如

我是h1

中的“我是h1”就是标记窗内容),取出标记窗的文字。

这种方法先取文章标题、HTML中所有的标记窗,在对其进行分词。然后计算标题的序列与标记窗文本序列的词语距离L,如果L小于一个阈值,则认为此标记窗内的文本是正文。

这种方法虽然看上去挺好,但其实也是存在问题的:

① 需要对页面中的所有文本进行分词,效率不高。

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

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

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

这种方法肯定是极好的,但是它需要先有html与正文数据,然后进行训练。我们在此不进行探讨。

5)基于行块分布函数正文提取

对于任意一个网页,它的正文和标签总是杂糅在一起。此方法的核心有亮点:① 正文区的密度;② 行块的长度;一个网页的正文区域肯定是文字信息分布最密集的区域之一,这个区域可能最大(评论信息长、正文较短),所以同时引进行块长度进行判断。

实现思路:

① 我们先将HTML去标签,只留所有正文,同时留下标签取出后的所有空白位置信息,我们称其为Ctext;

② 对每一个Ctext取周围k行(k<5),合起来称为Cblock;

③ 对Cblock去掉所有空白符,其文字总长度称为Clen;

④ 以Ctext为横坐标轴,以各行的Clen为纵轴,建立坐标系。

以这个网页为例:.htm该网页的正文区域为145行至182行。

由上图可知,正确的文本区域全都是分布函数图上含有最值且连续的一个区域,这个区域往往含有一个骤升点和一个骤降点。因此,网页正文抽取问题转化为了求行块分布函数上的骤升点和骤降点两个边界点,这两个边界点所含的区域包含了当前网页的行块长度最大值并且是连续的。

经过大量实验,证明此方法对于中文网页的正文提取有较高的准确度,此算法的优点在于,行块函数不依赖与HTML代码,与HTML标签无关,实现简单,准确率较高。

主要逻辑代码如下:

# 假设content为已经拿到的html# Ctext取周围k行(k<5),定为3blocksWidth = 3# 每一个Cblock的长度Ctext_len = []# Ctextlines = content.split( 'n')# 去空格fori inrange(len(lines)):iflines[i] == ' 'orlines[i] == 'n':lines[i] = ''# 计算纵坐标,每一个Ctext的长度fori inrange( 0, len(lines) - blocksWidth):wordsNum = 0forj inrange(i, i + blocksWidth):lines[j] = lines[j].replace( "s", "")wordsNum += len(lines[j])Ctext_len.append(wordsNum)# 开始标识start = -1# 结束标识end = -1# 是否开始标识boolstart = False# 是否结束标识boolend = False# 行块的长度阈值max_text_len = 88# 文章主内容main_text = []# 没有分割出Ctextiflen(Ctext_len) < 3:return'没有正文'fori inrange(len(Ctext_len) - 3):# 如果高于这个阈值if(Ctext_len[i] > max_text_len and( notboolstart)):# Cblock下面3个都不为0,认为是正文if(Ctext_len[i + 1] != 0orCtext_len[i + 2] != 0orCtext_len[i + 3] != 0):boolstart = Truestart = icontinueif(boolstart):# Cblock下面3个中有0,则结束if(Ctext_len[i] == 0orCtext_len[i + 1] == 0):end = iboolend = Truetmp = []# 判断下面还有没有正文if(boolend):forii inrange(start, end + 1):if(len(lines[ii]) < 5):continuetmp.append(lines[ii] + "n")str = "".join(list(tmp))# 去掉版权信息if( "Copyright"instr or"版权所有"instr):continuemain_text.append(str)boolstart = boolend = False# 返回主内容result = "".join(list(main_text))

0x4 结语

至此我们就可以获取任意内容的文章语料了,但这仅仅是开始,获取到了这些语料后我们还需要在一次进行清洗、分词、词性标注等,才能获得真正可以使用的语料。返回搜狐,查看更多