Python3网络爬虫开发实战(14)资讯类页面智能解析

页面智能解析就是利用算法从页面的 HTML 代码中提取想要的内容,算法会自动计算出目标内容在代码中的位置并将他们提取出来;

业界进展:DiffbotEmbedly ;目前 Diffbot 的提取效果算是比较先进的;

对于资讯类网站,除去一些特殊的页面(如登入页面,注册页面等),剩下页面可以分为两大类——列表页和详细页,前者提供多个详细页的索引导航信息,后者则包含具体的内容,针对这两类页面有以下几点需要解决:

  1. 详细页中文章标题,正文,发布事件和提取算法的实现;
  2. 列表页中链接列表的提取算法和实现;
  3. 如何判断一个页面是详细页还是列表页;

一、详细页智能解析算法

详细页是某个内容的展示页面,通常包含醒目的标题,发布事件和占据版面最大的正文部分。另外,详细页的侧栏通常会有一些关联或推荐的内容列表,页面头部的导航链接,评论区,广告区等;

一般来说,详细页包含的信息非常多,例如标题,发布时间,发布来源,作者,正文,封面图,评论数目,评论内容等,不过由于其中一些内容并不常用,而且提取算法大同小异,因此这里主要对三个信息进行提取,标题,正文和发布时间;

由于很多的网页都是由 JS 渲染而成的,因此通过请求获取的页面源代码不一定是在浏览器中看到的页面源代码,因此解析的前提是要求我们提取的必须是渲染完整的 HTML 代码

1.1 提取标题

详细页的标题一般包含在 title 节点或者 h 节点中,可以通过结合 title 节点和 h 节点的内容总结出两步提出思路:

  1. 提取页面中的 h 节点,将内容和 title 节点的文本做比对,和后者相似度最高的内容很可能就是详细页的标题
  2. 如果未在页面中找到 h 节点,则只能使用 title 节点的文本作为结果;

一般来说,有些网站为了使 SEO 效果比较好,会添加一些 meta 标签并将标题信息放入其中,因此总的来说可以综合三方面信息 title,h,meta 来获取信息;

from lxml.html import HtmlElement, fromstring  
  
METAS = [  
    '//meta[start-with](@property, "og:title")/@content',  
    '//meta[start-with](@name, "og:title")/@content',  
    '//meta[start-with](@property, "title")/@content',  
    '//meta[start-with](@name, "title")/@content',  
    '//meta[start-with](@property, "page:title")/@content',  
]  
  
  
def extract_by_meta(element: HtmlElement):  
    for xpath in METAS:  
        title = element.xpath(xpath)  
        if title:  
            return "".join(title)  
  
  
def extract_by_title(element: HtmlElement):  
    return "".join(element.xpath("//title//text()")).strip()  
  
  
def extract_by_h(element: HtmlElement):  
    hs = element.xpath("//h1//text()|//h2//text()|//h3//text()")  
    return hs or []  
  
  
def similarity(s1, s2):  
    if not s1 or not s2:  
        return 0  
    s1_set = set(list(s1))  
    s2_set = set(list(s2))  
    intersection = s1_set.intersection(s2_set)  
    union = s1_set.union(s2_set)  
    return len(intersection) / len(union)  
  
  
def extract_title(element: HtmlElement):  
    title_extracted_by_meta = extract_by_meta(element)  
    title_extracted_by_h = extract_by_h(element)  
    title_extracted_by_title = extract_by_title(element)  
  
    if title_extracted_by_meta:  
        return title_extracted_by_meta  
  
    title_extracted_by_h = sorted(  
        title_extracted_by_h,  
        key=lambda x: similarity(x, title_extracted_by_title),  
        reverse=True,  
    )  
  
    if title_extracted_by_h:  
        return title_extracted_by_h[0]  
  
    return title_extracted_by_title  
  
  
if __name__ == "__main__":  
    # 将html转化为xml格式  
    html = open("detail.html", encoding="utf-8").read()  
    element = fromstring(html=html)  
    title = extract_title(element)

1.2 提取正文

观察资讯类详细页中正文内容的特征,可以发现一些规律:

  1. 正文内容通常被包含在 body 节点的 p 节点中,而且 p 节点一般不会独立存在,而是存在于 div 等节点内;
  2. 正文内容所在的 p 节点也不一定全是正文内容,可能掺杂噪声,如网站的版权信息,发布人,文末广告等,这些都属于噪声;
  3. 正文内容所在的 p 节点中会夹杂 style,script 等节点,这些都不是正文内容;
  4. 正文内容所在的 p 节点内可能包含 code,span 等节点,这些内容大部分属于正文中的特殊样式字符,往往也需要归类到正文内容之中;

作者通过GeneralNewsExtractor基于文本及符号密度的网页正文提取方法的启发,得到了两个比较有效的正文文本提取依据指标——文本密度和符号密度;

文本密度不局限于纯文本和节点的大小比例,还考虑到了文本中包含的超链接,论文中定义,如果 i i i 为 HTML DOM 树种的一个节点,那么该节点的文本密度为:

T D i = T i − L T i T G i − L T G i TD_i = \frac{T_i-LT_i}{TG_i-LTG_i} TDi=TGiLTGiTiLTi

如下为其中各个符号的含义: T D i TD_i TDi 表示节点 i i i 的文本密度, T i T_i Ti 表示节点 i i i 中字符串的字数, L T i LT_i LTi 表示 i i i 中带链接的字符串的字数, T G i TG_i TGi 表示节点 i i i 中标签的数量, L T G i LTG_i LTGi 表示节点 i i i 中带链接的标签的数量;

正文中一般会带标点符号,而网页链接,广告信息由于文字较少,通常是不包含标点符号的,因此还可以借助符号密度排除一些内容;节点 i i i 的符号密度如下:

S b D i = T i − L T i S b i + 1 SbD_i=\frac{T_i-LT_i}{Sb_i + 1} SbDi=Sbi+1TiLTi
S b D i SbD_i SbDi 表示节点 i i i 的符号密度, T i T_i Ti 表示节点 i i i 中字符串的字数, L T i LT_i LTi 表示节点 i i i 中带链接的字符串的字数, S b i Sb_i Sbi 表示节点 i i i 中符号的数量(分母另外加 1 是为了确保除数不为 0 );

论文的作者经过多次实验。利用文本密度和符号密度相结合的方式提取正文信息能取得很不错的效果,可以结合两者为每个节点分别计算一个分数,分数最高的节点就为正文内容所在的节点,分数的计算公式如下: S c o r e i = l n S D × T D i × l g ( P N u m i + 2 ) × l n S b D i Score_i = lnSD \times TD_i \times lg(PNum_i + 2) \times lnSbD_i Scorei=lnSD×TDi×lg(PNumi+2)×lnSbDi
其中 S c o r e i Score_i Scorei 表示节点 i i i 的分数, S D SD SD 表示所有节点的文本密度标准差, T D i TD_i TDi 表示节点 i i i 的文本密度, P N u m i PNum_i PNumi 表示节点 i i i 包含的 p p p 节点的数量, S b D i SbD_i SbDi 表示节点 i i i 的符号密度;

如果需要追求更高的正确率,我们还可以结合 css 来利用视觉信息通过计算节点所占区域的大小来排除一些干扰;

from lxml.html import HtmlElement, etree  
  
  
CONTENT_USELESS_TAGS 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值