通用新闻网页正文抽取

优采云 发布时间: 2022-05-09 19:38

  通用新闻网页正文抽取

  通用新闻网页正文抽取

  爱数据LoveData

  cntongji

  原中国统计网(),大数据从业者,数据应用者,数据爱好者的学习、探讨、交流社区。定期组织线上与线下分享活动,邀请行业数据专家、技术大牛分享数据分析、BI工具、机器学习、行业数字化应用、大数据技术等数据领域专业知识。

  收录于合集

  

  最近有一个需求是抓取特定关键字对应的新闻列表,对于特定的网站,使用一套规则取匹配新闻正文非常容易,但是对于形形色色的新闻网页,并没有办法使用特定的规则完全匹配出结果。基于这样的需求,实现了这个新闻网站的匹配程序。

  具体思路如下:

  得到HTML源码后,首先去掉其中的script和link以及嵌套在网页中的iframe,这些内容对于大多数新闻网站的新闻正文并没有影响(我的目的是为了匹配出大多数新闻网站的新闻,并不是匹配出全部网站的正文)。

  去除网页中能够明显标识不属于正文的内容,如id或class为headerfootersidebar的div等等。

  计算每行以及附近n行的内容的文本长度,得到网页按行块分割的长度数组distribution。

  计算相邻行块间的长度差delta,并排序。

  对于大部分新闻网站,长度差为最大正值时,即得到新闻正文开始位置。长度差为最大负值时,即得到新闻正文结束位置

  上述过程的具体代码实现如下:

  第1/2步:

  <p style="line-height: 1.75em;">class HtmlExtractor(object):

   CARELESS_HTML_TAG_CLASS = [['iframe', 'script', 'style', 'link'],

                              ['header', 'footer', 'video', 'sidebar']]

   @classmethod

   def del_tags(cls, soup, tag_name):

       for tag in soup.find_all(tag_name):

           tag.decompose()

   @classmethod

   def del_tag_by_attr(cls, soup, tag_name, attrs):

       try:

           soup.find(tag_name, attrs=attrs).decompose()

       except:

           pass

   def __init__(self, html):

       soup = BeautifulSoup(html)

       try:

           self.title = soup.find('title').getText() + '\n\n\n'

       except:

           self.title = '\n\n\n'

       for tag in HtmlExtractor.CARELESS_HTML_TAG_CLASS[0]:

           HtmlExtractor.del_tags(soup, tag)

       for class_ in HtmlExtractor.CARELESS_HTML_TAG_CLASS[1]:

           HtmlExtractor.del_tag_by_attr(soup, 'div', {'id': class_})

           HtmlExtractor.del_tag_by_attr(soup, 'div', {'class': class_})

       self.text = soup.getText()

       self.lines = map(lambda x: re.sub(ur'\s+', u'', x), self.text.split('\n'))</p>

  第3步:

  <p style="line-height: 1.75em;">    BLOCK_WIDTH = 5

    def line_length(self, line):

       return len(self.lines[line])

   def parse(self):

       bw = HtmlExtractor.BLOCK_WIDTH

       distribution = []

       for pos in xrange(len(self.lines) - bw):

           distribution.append(reduce(int.__add__, map(lambda delta: self.line_length(pos + delta), xrange(bw))))

</p>

  第4步:

  <p style="line-height: 1.75em;">delta = sorted([(index, distribution[index + 1]-pos) for index, pos in enumerate(distribution[:-1])], cmp=lambda x, y: -cmp(x[-1], y[-1]))</p>

  第5步:

  <p style="line-height: 1.75em;">    left = delta[0][0]

   for index, dlt in enumerate(delta[1:-1]):

       left = (left, dlt[0])[left > dlt[0]]

       if len(str(dlt[-1])) != len(str(delta[index + 1])[-1]): break

   right = delta[-1][0]

   for pos in xrange(len(delta) - 1, 1, -1):

       right = (right, delta[pos][0])[right < delta[pos][0]]

       if len(str(delta[pos][-1])) != len(str(delta[pos - 1][-1])): break

   texts = [self.title]

   for line in xrange(left - bw, right + bw):

       if self.lines[line].strip():

           texts.append(self.lines[line])

   result = '\n\n'.join(texts)</p>

  End.

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线