我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何用Python实现网页正文的提取

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何用Python实现网页正文的提取

这篇文章主要介绍了如何用Python实现网页正文的提取的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何用Python实现网页正文的提取文章都会有所收获,下面我们一起来看看吧。

一个典型的新闻网页包括几个不同区域:

如何用Python实现网页正文的提取

新闻网页区域

我们要提取的新闻要素包含在:

  • 标题区域

  • meta数据区域(发布时间等)

  • 配图区域(如果想把配图也提取)

  • 正文区域

而导航栏区域、相关链接区域的文字就不属于该新闻的要素。

新闻的标题、发布时间、正文内容一般都是从我们抓取的html里面提取的。如果仅仅是一个网站的新闻网页,提取这三个内容很简单,写三个正则表达式就可以完美提取了。然而,我们的爬虫抓来的是成百上千的网站的网页。对这么多不同格式的网页写正则表达式会累死人的,而且网页一旦稍微改版,表达式可能就失效,维护这群表达式也是会累死人的。

累死人的做法当然想不通,我们就要探索一下好的算法来实现。

1. 标题的提取

标题基本上都会出现在html的<title>标签里面,但是又被附加了诸如频道名称、网站名称等信息;

标题还会出现在网页的“标题区域”。

那么这两个地方,从哪里提取标题比较容易呢?

网页的“标题区域”没有明显的标识,不同网站的“标题区域”的html代码部分千差万别。所以这个区域并不容易提取出来。

那么就只剩下<title>标签了,这个标签很容易提取,无论是正则表达式,还是lxml解析都很容易,不容易的是如何去除频道名称、网站名称等信息。

先来看看,<title>标签里面都是设么样子的附加信息:

  • 上海用“智慧”激活城市交通脉搏,让道路更安全更有序更通畅_浦江头条_澎湃新闻-The Paper

  • “沪港大学联盟”今天在复旦大学成立_教育_新民网

  • 三亚老人脚踹司机致公交车失控撞墙 被判刑3年_社会

  • 外交部:中美外交安全对话9日在美举行

  • 进博会:中国行动全球瞩目,中国担当世界点赞_南方观澜_南方网

  • 资本市场迎来重大改革 设立科创板有何深意?-新华网

观察这些title不难发现,新闻标题和频道名、网站名之间都是有一些连接符号的。那么我就可以通过这些连接符吧title分割,找出最长的部分就是新闻标题了。

这个思路也很容易实现,这里就不再上代码了,留给小猿们作为思考练习题自己实现一下。

2. 发布时间提取

发布时间,指的是这个网页在该网站上线的时间,一般它会出现在正文标题的下方——meta数据区域。从html代码看,这个区域没有什么特殊特征让我们定位,尤其是在非常多的网站版面面前,定位这个区域几乎是不可能的。这需要我们另辟蹊径。
跟标题一样,我们也先看看一些网站的发布时间都是怎么写的:

  • 央视网2018年11月06日 22:22

  • 时间:2018-11-07 14:27:00

  • 2018-11-07 11:20:37 来源: 新华网

  • 来源:中国日报网 2018-11-07 08:06:39

  • 2018年11月07日 07:39:19

  • 2018-11-06 09:58 来源:澎湃新闻

这些写在网页上的发布时间,都有一个共同的特点,那就是一个表示时间的字符串,年月日时分秒,无外乎这几个要素。通过正则表达式,我们列举一些不同时间表达方式(也就那么几种)的正则表达式,就可以从网页文本中进行匹配提取发布时间了。

这也是一个很容易实现的思路,但是细节比较多,表达方式要涵盖的尽可能多,写好这么一个提取发布时间的函数也不是那么容易的哦。小猿们尽情发挥动手能力,看看自己能写出怎样的函数实现。这也是留给小猿们的一道练习题。

3. 正文的提取

正文(包括新闻配图)是一个新闻网页的主体部分,它在视觉上占据中间位置,是新闻的内容主要的文字区域。正文的提取有很多种方法,实现上有复杂也有简单。本文介绍的方法,是结合老猿多年的实践经验和思考得出来的一个简单快速的方法,姑且称之为“节点文本密度法”。

我们知道,网页的html代码是由不同的标签(tag)组成了一个树状结构树,每个标签是树的一个节点。通过遍历这个树状结构的每个节点,找到文本最多的节点,它就是正文所在的节点。根据这个思路,我们来实现一下代码。

3.1 实现源码

#!/usr/bin/env python3#File: maincontent.py#Author: veelionimport reimport timeimport tracebackimport cchardetimport lxmlimport lxml.htmlfrom lxml.html import HtmlCommentREGEXES = {    'okMaybeItsACandidateRe': re.compile(        'and|article|artical|body|column|main|shadow', re.I),    'positiveRe': re.compile(        ('article|arti|body|content|entry|hentry|main|page|'         'artical|zoom|arti|context|message|editor|'         'pagination|post|txt|text|blog|story'), re.I),    'negativeRe': re.compile(        ('copyright|combx|comment|com-|contact|foot|footer|footnote|decl|copy|'         'notice|'         'masthead|media|meta|outbrain|promo|related|scroll|link|pagebottom|bottom|'         'other|shoutbox|sidebar|sponsor|shopping|tags|tool|widget'), re.I),}class MainContent:    def __init__(self,):        self.non_content_tag = set([            'head',            'meta',            'script',            'style',            'object', 'embed',            'iframe',            'marquee',            'select',        ])        self.title = ''        self.p_space = re.compile(r'\s')        self.p_html = re.compile(r'<html|</html>', re.IGNORECASE|re.DOTALL)        self.p_content_stop = re.compile(r'正文.*结束|正文下|相关阅读|声明')        self.p_clean_tree = re.compile(r'author|post-add|copyright')    def get_title(self, doc):        title = ''        title_el = doc.xpath('//title')        if title_el:            title = title_el[0].text_content().strip()        if len(title) < 7:            tt = doc.xpath('//meta[@name="title"]')            if tt:                title = tt[0].get('content', '')        if len(title) < 7:            tt = doc.xpath('//*[contains(@id, "title") or contains(@class, "title")]')            if not tt:                tt =  doc.xpath('//*[contains(@id, "font01") or contains(@class, "font01")]')            for t in tt:                ti = t.text_content().strip()                if ti in title and len(ti)*2 > len(title):                    title = ti                    break                if len(ti) > 20: continue                if len(ti) > len(title) or len(ti) > 7:                    title = ti        return title    def shorten_title(self, title):        spliters = [' - ', '–', '—', '-', '|', '::']        for s in spliters:            if s not in title:                continue            tts = title.split(s)            if len(tts) < 2:                continue            title = tts[0]            break        return title    def calc_node_weight(self, node):        weight = 1        attr = '%s %s %s' % (            node.get('class', ''),            node.get('id', ''),            node.get('style', '')        )        if attr:            mm = REGEXES['negativeRe'].findall(attr)            weight -= 2 * len(mm)            mm = REGEXES['positiveRe'].findall(attr)            weight += 4 * len(mm)        if node.tag in ['div', 'p', 'table']:            weight += 2        return weight    def get_main_block(self, url, html, short_title=True):        ''' return (title, etree_of_main_content_block)        '''        if isinstance(html, bytes):            encoding = cchardet.detect(html)['encoding']            if encoding is None:                return None, None            html = html.decode(encoding, 'ignore')        try:            doc = lxml.html.fromstring(html)            doc.make_links_absolute(base_url=url)        except :            traceback.print_exc()            return None, None        self.title = self.get_title(doc)        if short_title:            self.title = self.shorten_title(self.title)        body = doc.xpath('//body')        if not body:            return self.title, None        candidates = []        nodes = body[0].getchildren()        while nodes:            node = nodes.pop(0)            children = node.getchildren()            tlen = 0            for child in children:                if isinstance(child, HtmlComment):                    continue                if child.tag in self.non_content_tag:                    continue                if child.tag == 'a':                    continue                if child.tag == 'textarea':                    # FIXME: this tag is only part of content?                    continue                attr = '%s%s%s' % (child.get('class', ''),                                   child.get('id', ''),                                   child.get('style'))                if 'display' in attr and 'none' in attr:                    continue                nodes.append(child)                if child.tag == 'p':                    weight = 3                else:                    weight = 1                text = '' if not child.text else child.text.strip()                tail = '' if not child.tail else child.tail.strip()                tlen += (len(text) + len(tail)) * weight            if tlen < 10:                continue            weight = self.calc_node_weight(node)            candidates.append((node, tlen*weight))        if not candidates:            return self.title, None        candidates.sort(key=lambda a: a[1], reverse=True)        good = candidates[0][0]        if good.tag in ['p', 'pre', 'code', 'blockquote']:            for i in range(5):                good = good.getparent()                if good.tag == 'div':                    break        good = self.clean_etree(good, url)        return self.title, good    def clean_etree(self, tree, url=''):        to_drop = []        drop_left = False        for node in tree.iterdescendants():            if drop_left:                to_drop.append(node)                continue            if isinstance(node, HtmlComment):                to_drop.append(node)                if self.p_content_stop.search(node.text):                    drop_left = True                continue            if node.tag in self.non_content_tag:                to_drop.append(node)                continue            attr = '%s %s' % (                node.get('class', ''),                node.get('id', '')            )            if self.p_clean_tree.search(attr):                to_drop.append(node)                continue            aa = node.xpath('.//a')            if aa:                text_node = len(self.p_space.sub('', node.text_content()))                text_aa = 0                for a in aa:                    alen = len(self.p_space.sub('', a.text_content()))                    if alen > 5:                        text_aa += alen                if text_aa > text_node * 0.4:                    to_drop.append(node)        for node in to_drop:            try:                node.drop_tree()            except:                pass        return tree    def get_text(self, doc):        lxml.etree.strip_elements(doc, 'script')        lxml.etree.strip_elements(doc, 'style')        for ch in doc.iterdescendants():            if not isinstance(ch.tag, str):                continue            if ch.tag in ['div', 'h2', 'h3', 'h4', 'p', 'br', 'table', 'tr', 'dl']:                if not ch.tail:                    ch.tail = '\n'                else:                    ch.tail = '\n' + ch.tail.strip() + '\n'            if ch.tag in ['th', 'td']:                if not ch.text:                    ch.text = '  '                else:                    ch.text += '  '            # if ch.tail:            #     ch.tail = ch.tail.strip()        lines = doc.text_content().split('\n')        content = []        for l in lines:            l = l.strip()            if not l:                continue            content.append(l)        return '\n'.join(content)    def extract(self, url, html):        '''return (title, content)        '''        title, node = self.get_main_block(url, html)        if node is None:            print('\tno main block got !!!!!', url)            return title, '', ''        content = self.get_text(node)        return title, content

3.2 代码解析

跟新闻爬虫一样,我们把整个算法实现为一个类:MainContent。

首先,定义了一个全局变量: REGEXES。它收集了一些经常出现在标签的class和id中的关键词,这些词标识着该标签可能是正文或者不是。我们用这些词来给标签节点计算权重,也就是方法calc_node_weight()的作用。

MainContent类的初始化,先定义了一些不会包含正文的标签 self.non_content_tag,遇到这些标签节点,直接忽略掉即可。

本算法提取标题实现在get_title()这个函数里面。首先,它先获得<title>标签的内容,然后试着从<meta>里面找title,再尝试从<body>里面找id和class包含title的节点,最后把从不同地方获得的可能是标题的文本进行对比,最终获得标题。对比的原则是:

  • <meta><body>里面找到的疑似标题如果包含在<title>标签里面,则它是一个干净(没有频道名、网站名)的标题;

  • 如果疑似标题太长就忽略

  • 主要把<title>标签作为标题

<title>标签里面获得标题,就要解决标题清洗的问题。这里实现了一个简单的方法: clean_title()。

在这个实现中,我们使用了lxml.html把网页的html转化成一棵树,从body节点开始遍历每一个节点,看它直接包含(不含子节点)的文本的长度,从中找出含有最长文本的节点。这个过程实现在方法:get_main_block()中。其中一些细节,小猿们可以仔细体会一下。

其中一个细节就是,clean_node()这个函数。通过get_main_block()得到的节点,有可能包含相关新闻的链接,这些链接包含大量新闻标题,如果不去除,就会给新闻内容带来杂质(相关新闻的标题、概述等)。

还有一个细节,get_text()函数。我们从main block中提取文本内容,不是直接使用text_content(),而是做了一些格式方面的处理,比如在一些标签后面加入换行符合\n,在table的单元格之间加入空格。这样处理后,得到的文本格式比较符合原始网页的效果。

爬虫知识点

cchardet模块
用于快速判断文本编码的模块

lxml.html模块
结构化html代码的模块,通过xpath解析网页的工具,高效易用,是写爬虫的居家必备的模块。

内容提取的复杂性
我们这里实现的正文提取的算法,基本上可以正确处理90%以上的新闻网页。
但是,世界上没有千篇一律的网页一样,也没有一劳永逸的提取算法。大规模使用本文算法的过程中,你会碰到奇葩的网页,这个时候,你就要针对这些网页,来完善这个算法类。

关于“如何用Python实现网页正文的提取”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“如何用Python实现网页正文的提取”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何用Python实现网页正文的提取

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何用Python实现网页正文的提取

这篇文章主要介绍了如何用Python实现网页正文的提取的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何用Python实现网页正文的提取文章都会有所收获,下面我们一起来看看吧。一个典型的新闻网页包括几个不同区域
2023-06-02

【Python】提取网页正文内容的相关模

【Python】提取网页正文内容的相关模块与技术  1、正文抽取地址  https://github.com/buriy/python-readability  【安装】  pip install readability-lxml  【测试
2023-01-31

如何用php提取网页所有文字

要使用PHP提取网页上的所有文字,可以使用PHP的DOMDocument和XPath库来解析HTML页面并提取文本内容。以下是一个示例代码:```php// 网页地址$url = 'http://example.com';// 创建DOMD
2023-09-26

python如何实现基本的抓取网页

这篇文章主要介绍了python如何实现基本的抓取网页,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。基本抓取网页get方法import urllib2url = "http:/
2023-06-17

python爬取网页如何创建文件

要在Python中爬取网页并创建文件,可以按照以下步骤进行:1. 导入必要的库:```pythonimport requests```2. 发送GET请求以获取网页内容:```pythonurl = "https://www.example
2023-09-15

php如何实现提取域名及跳转到指定网页

这篇“php如何实现提取域名及跳转到指定网页”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“php如何实现提取域名及跳转到指定
2023-07-05

如何用python爬取网页数据

要用Python爬取网页数据,可以使用Python的一些库和模块,例如requests、BeautifulSoup和Scrapy等。下面是一个简单的示例,使用requests和BeautifulSoup库来爬取网页数据:```pythoni
2023-10-12

Python如何实现文件的读取

这篇文章将为大家详细讲解有关Python如何实现文件的读取,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一·Numpy库中操作文件1.操作csv文件import numpy as npa=np.rando
2023-06-22

怎么用python实现精准搜索并提取网页核心内容

这篇文章主要讲解了“怎么用python实现精准搜索并提取网页核心内容”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用python实现精准搜索并提取网页核心内容”吧!生成PDF开始想了一个
2023-06-25

如何使用hta提取网易文件

小编给大家分享一下如何使用hta提取网易文件,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!网易文件摄取 2023-06-08

如何使用Python实现提取语句中的人名

这篇文章主要介绍如何使用Python实现提取语句中的人名,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!环境依赖需要安装两个库,其实一个也可以,但是我这边准备了两个库做个比较。安装命令如下:pip install LA
2023-06-29

python如何用正则表达式提取字符串

这篇文章主要介绍了python如何用正则表达式提取字符串问题,具有很好的参考价值,希望对大家有所帮助。
2023-03-02

如何用Python获取网页指定内容

这篇文章主要介绍“如何用Python获取网页指定内容”,在日常操作中,相信很多人在如何用Python获取网页指定内容问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何用Python获取网页指定内容”的疑惑有所
2023-06-29

教你如何用CSS实现网页文字隐藏

编程学习网:现在的网页设计越来越动态化,如果我们既想实现网页的动态化,又要兼顾友好搜索引擎,就不得不对网页添加一些文字,但这些文字的存在明显与我们动态网页的风格格格不入。怎么办呢?解决的方法是把这些文字进行隐藏。针对这种需求小编为大家网罗了几种css隐藏网页文字的方法,有需要的的朋友不妨参考一下。
教你如何用CSS实现网页文字隐藏
2024-04-23

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录