用python3教你任意Html主内容提取功能

本文将和大家分享一些从互联网上爬取语料的经验。

0x1 工具准备

工欲善其事必先利其器,爬取语料的根基便是基于python。

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

简单介绍一个各模块的功能

01|requests

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

我们简单的比较一下:

urllib:

 import urllib2
 import urllib
 URL_GET = "https://api.douban.com/v2/event/list"
 #构建请求参数
 params = urllib.urlencode({'loc':'108288','day_type':'weekend','type':'exhibition'})
 #发送请求
 response = urllib2.urlopen('?'.join([URL_GET,'%s'])%params)
#Response Headers
print(response.info())
#Response Code
print(response.getcode())
#Response Body
print(response.read())

requests:

import requests
 URL_GET = "https://api.douban.com/v2/event/list"
 #构建请求参数
 params = {'loc':'108288','day_type':'weekend','type':'exhibition'}

 #发送请求
 response = requests.get(URL_GET,params=params)
 #Response Headers
print(response.headers)
#Response Code
print(response.status_code)
#Response Body
print(response.text)

我们可以发现,这两种库还是有一些区别的:

1. 参数的构建:urllib需要对参数进行urlencode编码处理,比较麻烦;requests无需额外编码处理,十分简洁。

2. 请求发送:urllib需要额外对url参数进行构造,变为符合要求的形式;requests则简明很多,直接get对应链接与参数。

3. 连接方式:看一下返回数据的头信息的“connection”,使用urllib库时,"connection":"close",说明每次请求结束关掉socket通道,而使用requests库使用了urllib3,多次请求重复使用一个socket,"connection":"keep-alive",说明多次请求使用一个连接,消耗更少的资源

4. 编码方式:requests库的编码方式Accept-Encoding更全,在此不做举例

综上所诉,使用requests更为简明、易懂,极大的方便我们开发。

02|lxml

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

当我们拿到requests返回的页面后,我们怎么拿到想要的数据呢?这个时候祭出lxml这强大的HTML/XML解析工具。python从不缺解析库,那么我们为什么要在众多库里选择lxml呢?我们选择另一款出名的HTML解析库BeautifulSoup来进行对比。

我们简单的比较一下:

BeautifulSoup:

from bs4 import BeautifulSoup #导入库
# 假设html是需要被解析的html
#将html传入BeautifulSoup 的构造方法,得到一个文档的对象
soup = BeautifulSoup(html,'html.parser',from_encoding='utf-8')
#查找所有的h4标签
links = soup.find_all("h4")

lxml:

from lxml import etree
# 假设html是需要被解析的html
#将html传入etree 的构造方法,得到一个文档的对象
root = etree.HTML(html)
#查找所有的h4标签
links = 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。

这两种库,无论是import模块速度,还是编码、解码速度,都是simplejson更胜一筹,再加上兼容性 simplejson 更好。所以大家如果想使用方库,可以使用simplejson。

0x2 确定语料源

将武器准备好之后,接下来就需要确定爬取方向。

以电竞类语料为例,现在我们要爬电竞类相关语料。大家熟悉的电竞平台有企鹅电竞、企鹅电竞和企鹅电竞(斜眼),所以我们以企鹅电竞上直播的游戏作为数据源进行爬取。

我们登陆企鹅电竞官网,进入游戏列表页,可以发现页面上有很多游戏,通过人工去写这些游戏名收益明显不高,于是我们就开始我们爬虫的第一步:游戏列表爬取。

import requests
 from lxml import etree
 # 更新游戏列表
 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 = 'https://egame.qq.com/gamelist'
  # 不压缩html,最大链接时间为10妙
  res = requests.get(url, headers=heads, verify=False, timeout=10)
  # 为防止出错,编码utf-8
  res.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</h1> 中的“我是h1”就是标记窗内容),取出标记窗的文字。

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

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

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

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

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

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

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

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

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

实现思路:

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

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

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

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

以这个网页为例: http://www.gov.cn/ldhd/2009-11/08/content_1459564.htm   该网页的正文区域为145行至182行。

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

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

主要逻辑代码如下:

# 假设content为已经拿到的html
 # Ctext取周围k行(k<5),定为3
 blocksWidth = 3
 # 每一个Cblock的长度
 Ctext_len = []
 # Ctext
 lines = content.split('n')
 # 去空格
for i in range(len(lines)):
  if lines[i] == ' ' or lines[i] == 'n':
    lines[i] = ''
# 计算纵坐标,每一个Ctext的长度
for i in range(0, len(lines) - blocksWidth):
  wordsNum = 0
  for j in range(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 = []
# 没有分割出Ctext
if len(Ctext_len) < 3:
  return '没有正文'
for i in range(len(Ctext_len) - 3):
  # 如果高于这个阈值
  if(Ctext_len[i] > max_text_len and (not boolstart)):
    # Cblock下面3个都不为0,认为是正文
    if (Ctext_len[i + 1] != 0 or Ctext_len[i + 2] != 0 or Ctext_len[i + 3] != 0):
      boolstart = True
      start = i
      continue
  if (boolstart):
    # Cblock下面3个中有0,则结束
    if (Ctext_len[i] == 0 or Ctext_len[i + 1] == 0):
      end = i
      boolend = True
  tmp = []
  # 判断下面还有没有正文
  if(boolend):
    for ii in range(start, end + 1):
      if(len(lines[ii]) < 5):
        continue
      tmp.append(lines[ii] + "n")
    str = "".join(list(tmp))
    # 去掉版权信息
    if ("Copyright" in str or "版权所有" in str):
      continue
    main_text.append(str)
    boolstart = boolend = False
# 返回主内容
result = "".join(list(main_text))

0x4 结语

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

总结

以上所述是小编给大家介绍的用python3教你任意Html主内容提取功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Python中使用HTMLParser解析html实例

    前几天遇到一个问题,需要把网页中的一部分内容挑出来,于是找到了urllib和HTMLParser两个库.urllib可以将网页爬下来,然后交由HTMLParser解析,初次使用这个库,在查官方文档时也遇到了一些问题,在这里写下来与大家分享. 一个例子 复制代码 代码如下: from HTMLParser import HTMLParser class MyHTMLParser(HTMLParser):   def handle_starttag(self, tag, attrs):     pr

  • Python利用BeautifulSoup解析Html的方法示例

    介绍 Beautiful Soup提供一些简单的.python式的函数用来处理导航.搜索.修改分析树等功能.它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序. Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码.你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了.然后,你仅仅需要说明一下原始编码方式就可以了. Beautiful

  • 对Python3 解析html的几种操作方式小结

    解析html是爬虫后的重要的一个处理数据的环节.一下记录解析html的几种方式. 先介绍基础的辅助函数,主要用于获取html并输入解析后的结束 #把传递解析函数,便于下面的修改 def get_html(url, paraser=bs4_paraser): headers = { 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, sdch', 'Accept-Language': 'zh-CN,zh;q=0.8', 'Host': 'www.

  • 用python3教你任意Html主内容提取功能

    本文将和大家分享一些从互联网上爬取语料的经验. 0x1 工具准备 工欲善其事必先利其器,爬取语料的根基便是基于python. 我们基于python3进行开发,主要使用以下几个模块:requests.lxml.json. 简单介绍一个各模块的功能 01|requests requests是一个Python第三方库,处理URL资源特别方便.它的官方文档上写着大大口号:HTTP for Humans(为人类使用HTTP而生).相比python自带的urllib使用体验,笔者认为requests的使用体

  • Python3实现打印任意宽度的菱形代码

    我就废话不多说了,还是直接看代码吧! width = 11 #注意宽度必须位奇数 for i in range(-width//2,width//2+1): prespace=i if i>0 else -i print(' '*prespace+'*'*(width-prespace*2)) 运行结果: * *** ***** ******* ********* *********** ********* ******* ***** *** * 补充知识:Python打印高度为2 * n +

  • 易语言高级表格选择任意行列获取内容的代码示例

    高级表格取选择任意行列的内容 .版本 2 .支持库 eGrid .程序集 窗口程序集1 .子程序 __启动窗口_创建完毕 高级表格1.置数据 (1, 1, #表格常量.文本型, "我") 高级表格1.置数据 (1, 2, #表格常量.文本型, "爱") 高级表格1.置数据 (1, 3, #表格常量.文本型, "你") 高级表格1.置数据 (1, 4, #表格常量.文本型, "你") 高级表格1.置数据 (1, 5, #表格常量

  • 教你怎么使用hadoop来提取文件中的指定内容

    一.需求 把以下txt中含"baidu"字符串的链接输出到一个文件,否则输出到另外一个文件. 二.步骤 1.LogMapper.java package com.whj.mapreduce.outputformat; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.

  • PHP直接修改表内容DataGrid功能实现代码

    由于需要连接Oracle所以从二次开发和页面样式来说个人觉得phpMyDataGrid还是比较好上手. 1. 创建测试数据库和表 create database `guru`; USE `guru`; CREATE TABLE `employees` ( `id` int(6) NOT NULL auto_increment, `name` char(20) default NULL, `lastname` char(20) default NULL, `salary` float defaul

  • Python3 selenium 实现QQ群接龙自动化功能

    一.环境 环境配置为安装了 selenium 模块的 Python3 ,以及浏览器对应的driver 如果没有安装 selenium ,可以在控制台执行下面的代码 pip3 install selenium 浏览器driver下载地址:https://selenium-python.readthedocs.io/installation.html#drivers 需要选择对应的浏览器的对应版本进行下载 下载完成之后放到Python安装目录即可 二.代码 不足:只能给最新发布的一个群接龙进行自动接

  • 手把手教你利用opencv实现人脸识别功能(附源码+文档)

    目录 一.环境 二.使用Haar级联进行人脸检测 三.Haar级联结合摄像头 四.使用SSD的人脸检测 五. SSD结合摄像头人脸检测 六.结语 一.环境 pip install opencv-python python3.9 pycharm2020 人狠话不多,直接上代码,注释在代码里面,不说废话. 二.使用Haar级联进行人脸检测 测试案例: 代码:(记得自己到下载地址下载对应的xml) # coding=gbk """ 作者:川川 @时间 : 2021/9/5 16:3

  • Python实现B站UP主自动监控功能详解

    目录 开发工具 环境搭建 原理简介 1.确定小目标 2.模拟登录 3.自动关注 4.实时监控 效果展示 众所周知,B站有很多有趣的UP主,可以教大家一些"实用"的知识: 但是他们一般都没有固定的更新时间,那么如何才能第一时间知道自己又有新的饭点可以看的下饭素材呢?当然是用python来写个脚本自动监控UP是否更新了视频,并自动下载啦~ 废话不多说,让我们愉快地开始吧~ 开发工具 Python版本:3.7.8 相关模块: DecryptLogin模块: videofetch模块: 以及一

  • 教你使用pyqt实现桌面歌词功能

    目录 前言 代码实现 后记 前言 酷狗.网抑云和 QQ 音乐都有桌面歌词功能,这篇博客也将使用 pyqt 实现桌面歌词功能,效果如下图所示: 代码实现 桌面歌词部件 LyricWidget 在 paintEvent 中绘制歌词.我们可以直接使用 QPainter.drawText 来绘制文本,但是通过这种方式无法对歌词进行描边.所以这里更换为 QPainterPath 来实现,使用 QPainterPath.addText 将歌词添加到绘制路径中,接着使用 Qainter.strokePath 

  • Python3.4编程实现简单抓取爬虫功能示例

    本文实例讲述了Python3.4编程实现简单抓取爬虫功能.分享给大家供大家参考,具体如下: import urllib.request import urllib.parse import re import urllib.request,urllib.parse,http.cookiejar import time def getHtml(url): cj=http.cookiejar.CookieJar() opener=urllib.request.build_opener(urllib.

随机推荐