/news-spider

新闻爬虫,无需编写配置规则,可抓取任意新闻网站

Primary LanguagePython

新闻爬虫

功能:无需编写配置规则,可抓取任意新闻网站静态页面的标题、时间、作者、正文信息

背景

互联网时代,信息庞杂且有充满价值。拿新闻网站来说,网页模板多种多样,那么如何对成千上万个新闻网站进行抓取呢?有两种方式:

  1. 依次编写每个网站每个模板的提取规则,然后写个框架来加载这些规则,进行抓取
  2. 开发个提取算法,可提取任意网页的新闻结构化信息

方法1提取精度高,但开发成本和维护成本较高,且开发配置周期较长

方法2为本文所讲述方法,维护成本低,无需配置,可抓取海量新闻

1.标题提取

1.1 如果是特殊的网站,使用指定的正则提取

1.2 否则在网页源代码中的title标签中提取,并去掉_-|之后的内容。多为网站名,如:

谱写美丽**的海南篇章--时政--人民网

1.3 如经过以上两步还没有匹配到标题,则在h1~h4标题标签中提取

2.时间提取

2.1 枚举时间正则

[
  "(\d{4}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[0-1]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{4}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[2][0-3]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{4}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[0-1]?[0-9]:[0-5]?[0-9])",
  "(\d{4}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[2][0-3]:[0-5]?[0-9])",
  "(\d{4}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[1-24]\d时[0-60]\d分)([1-24]\d时)",
  "(\d{2}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[0-1]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{2}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[2][0-3]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{2}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[0-1]?[0-9]:[0-5]?[0-9])",
  "(\d{2}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[2][0-3]:[0-5]?[0-9])",
  "(\d{2}[-|/|.]\d{1,2}[-|/|.]\d{1,2}\s*?[1-24]\d时[0-60]\d分)([1-24]\d时)",
  "(\d{4}年\d{1,2}月\d{1,2}日\s*?[0-1]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{4}年\d{1,2}月\d{1,2}日\s*?[2][0-3]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{4}年\d{1,2}月\d{1,2}日\s*?[0-1]?[0-9]:[0-5]?[0-9])",
  "(\d{4}年\d{1,2}月\d{1,2}日\s*?[2][0-3]:[0-5]?[0-9])",
  "(\d{4}年\d{1,2}月\d{1,2}日\s*?[1-24]\d时[0-60]\d分)([1-24]\d时)",
  "(\d{2}年\d{1,2}月\d{1,2}日\s*?[0-1]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{2}年\d{1,2}月\d{1,2}日\s*?[2][0-3]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{2}年\d{1,2}月\d{1,2}日\s*?[0-1]?[0-9]:[0-5]?[0-9])",
  "(\d{2}年\d{1,2}月\d{1,2}日\s*?[2][0-3]:[0-5]?[0-9])",
  "(\d{2}年\d{1,2}月\d{1,2}日\s*?[1-24]\d时[0-60]\d分)([1-24]\d时)",
  "(\d{1,2}月\d{1,2}日\s*?[0-1]?[0-9]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{1,2}月\d{1,2}日\s*?[2][0-3]:[0-5]?[0-9]:[0-5]?[0-9])",
  "(\d{1,2}月\d{1,2}日\s*?[0-1]?[0-9]:[0-5]?[0-9])",
  "(\d{1,2}月\d{1,2}日\s*?[2][0-3]:[0-5]?[0-9])",
  "(\d{1,2}月\d{1,2}日\s*?[1-24]\d时[0-60]\d分)([1-24]\d时)",
  "(\d{4}[-|/|.]\d{1,2}[-|/|.]\d{1,2})",
  "(\d{2}[-|/|.]\d{1,2}[-|/|.]\d{1,2})",
  "(\d{4}年\d{1,2}月\d{1,2}日)",
  "(\d{2}年\d{1,2}月\d{1,2}日)",
  "(\d{1,2}月\d{1,2}日)"
]

2.2 在正文附近提取(正文位置判断见 4.正文提取),这里我在正文上下10行内提取。如正文在 2030 行 ,则在1040行之内提取发布时间。(正文开始和结束段落位置不能保证百分百准确,所以暂时没有采用从正文开始位置往前逐行提取时间的方法)

2.3 若正文附近没有提取到时间,则在网页源代码中提取。

2.4 提取时间时,网页源代码需要把html标签替换为<>,因为html标签里边的文字可能符合时间正则,会被误提取出来,导致时间不准确

3.作者提取

3.1 同时间提取类似,枚举作者正则

[
  "责编[:|:| |丨|/]\s*([\u4E00-\u9FA5]{2,5})[^\u4e00-\u9fa5|:|:]",
  "作者[:|:| |丨|/]\s*([\u4E00-\u9FA5]{2,5})[^\u4e00-\u9fa5|:|:]",
  "编辑[:|:| |丨|/]\s*([\u4E00-\u9FA5]{2,5})[^\u4e00-\u9fa5|:|:]",
  "文[:|:| |丨|/]\s*([\u4E00-\u9FA5]{2,5})[^\u4e00-\u9fa5|:|:]",
  "撰文[:|:| |丨|/]\s*([\u4E00-\u9FA5]{2,5})[^\u4e00-\u9fa5|:|:]"
]

3.2 在网页源代码中,根据以上正则提取作者信息

3.3 若没有匹配到,则将html标签替换为空格后进一步匹配,因为有的作者和名字中间有标签

3.4 仍没有匹配到,则在html的author标签中提取,正则为(?i)<meta.*?author.*?content="(.*?)"

4.正文提取

4.1 正文提取的方法

  1. 基于标签用途的正文提取算法(比如title或h1,h2标签一般用作标题,p一般表示正文段落,根据标签的含义去提取正文)
  2. 基于文本密度的正文提取算法(正文通常文字比较密集)
  3. 基于数据挖掘**的网页正文抽取方法
  4. 基于视觉网页块分析技术的正文抽取

方法1 准确度不高,3、4复杂度及学习成本高。此处采用方法2基于文本密度算法提取正文,算法通俗易懂、提取速度快

4.2 基于文本密度正文提取算法讲解

4.1.1 论证:

由算法名的表面意思,我们可得知,正文与文本密度有关。统计一篇网上的新闻 http://news.sohu.com/20131229/n392604462.shtml, 行号与字数的对应关系如下(不包含html标签):

-w637

从图中可以看出207~242行之间文字比较密集,而事实上这段区间的内容正好是正文。

4.1.2 算法描述

  1. 处理html源代码:将html去标签,保留段落标签、图片标签等。将空格和换行符外的其他空白符去掉,空格在提取日期和作者中会用到,换行符用于分隔每行;

  2. 统计连续n段文字的总长度,此处用于形容一定区域的文本密度;

  3. 将文本最密集处当成正文的所在位置。如第i行~i+x行这段区间文本最密集,则初步将第i行当做正文所在位置;

  4. 从正文所在位置向上查找、找到文本块密度小于等于正文文本密度阈值时,算为正文起始位置;

  5. 在正文所在位置向下查找、找到文本密度小于等于正文文本密度阈值时,算为正文结束位置;

  6. 判断是否为正文:正文一般都包含p标签。此处统计p标签内的文字个数占正文总文字数的比例,所占比例超过一定阈值且正文字数大于一定阈值,则算为正文

举例说明:

下图为 http://news.sohu.com/20131229/n392604462.shtml 文本块密度统计,这里将连续10行算一个文本块。则下标为0的文本块所在行数为0~9,下标为1的文本块所在行数为1~10,下标为2的文本块所在行数为2~11... 以此类推(文本块下标与行号相吻合, 一个段落为一行)

-w701

从图中可以看出下标为221的文本块文本密度最高。此时,将算法描述步骤3的i值设为221。然后参考步骤4,向前找文本密度块小于正文文本密度阈值的行号,为117行。最后参考步骤5,向后找文本密度块小于正文文本密度阈值的行号,为256行。这与行内文本密度统计图相吻合。所以117行到256行为新闻正文。

问题1:为什么不直接用行内文本密度的方法直接找正文所在位置,而是引用了文本块的概念?

: 因为网页多种多样,有的正文段落可能就是个图片,有的正文段落就几个字,也有的段落文字虽多但可能是导航或外链等信息。我们不能单纯的用最长段落做为正文所在位置,也不能单单的判断段落长度来定位正文的开始与结束位置。我们需要分析正文的特征,正文部分连续n段文本字数总和比较高,非正文部分连续n段文本字数总和比较低。我们根据文字最密集的文本块来定位正文所在位置,根据一定的阈值来分隔正文文本块与非正文部分。

问题2:为什么最后用p标签内文字个数占正文总文字数的比例作为判断是否为正文的依据?

: p标签为段落标签,大多数新闻的正文段落都在p标签里。相反一些外链等文字一般在a标签里。我们用段落占比来判断是不是正文,并根据正文总字数删选,过滤掉新闻首页,中间导航页等非新闻信息。

4.1.3 算法实现

github: https://github.com/Boris-code/news-spider

总结

本文讲解了面对海量新闻,庞杂的页面模板,如何通用的进行抓取,代码样例采用boris-spider爬虫框架,为大家演示了个小demo,若想全站抓取,可以二次开发下,引入抓取深度的概念。当然,boris-spider框架也是支持的

了解更多

欢迎加入知识星球 https://t.zsxq.com/eEmAeae

知识星球

本星球专注于爬虫技术分享,通过一些案例详细讲解爬虫中遇到的问题以及解决手段。涉及的知识包括但不限于 爬虫框架刨析、js逆向、中间人、selenium 、pyppeteer、Android 逆向!期待您的加入,和我们一起探讨爬虫技术,拓展爬虫思维!

QQ 群:750614606

WechatIMG188