通用新闻网页正文抽取
优采云 发布时间: 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.