啰里啰嗦写了好几篇了,也不知道对大家有没有帮助,这一篇应该是该系列教程的最后一篇了,希望有个好的结尾!上一篇中我们简单介绍了解析新闻索引中的新闻版块,新闻标题和新闻URL的信息,这一篇我们将根据这些信息来逐步获取真正的新闻内容。
首先,我们重新定义一个函数来获取长沙晚报新闻网的带绝对路径的URL地址,总共有两个URL,一个是新闻索引URL,一个是新闻URL. 代码如下:
def get_absolute_url(type, when, filename = None):
if not isinstance(when, date):
return None
if type == 'news' and filename is None:
return None
year = when.strftime("%Y")
month = when.strftime("%m")
day = when.strftime("%d")
prefix = 'http://cswb.changsha.cn/html/%s-%s/%s/' % (year, month, day)
if type == 'index':
filename = 'index_%s-%s-%s.htm' % (year, month, day)
return "%s/%s" % (prefix, filename)
然后我们定义获取web页面的函数如下:
def fetch_url(url):
http_headers = {'User-Agen':"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:15.0) Gecko/20100101 Firefox/15.0.1"}
req = urllib2.Request(url, None, http_headers)
try:
response = urllib2.urlopen(req)
except IOError, e:
print "We failed to open %s." % index_URL
if hasattr(e, 'code'):
print "We failed with code - %s" % e.code
elif hasattr(e, 'reason'):
print "We failed to reach a server."
print "This usually means the server doesn't exist/down,or we don't have an internet connection."
print "The reason is $s" % e.reason
return False
else:
print "Here are the headers of the page:"
print response.info()
print "These are the cookies we have received so far:"
for i, cookie in enumerate(cj):
print i, ':', cookie
cj.save(COOKIEFILE)
return response.read()
在上一篇中我们已经实现了获取新闻版块、新闻标题和新闻URL这三类信息的函数,这一篇中我们将添加获取新闻内容的函数:
def get_news_content(htmlstring):
content = None
# remove 'n' or 'r', otherwise the xpath will intrepret it
str = clean_html(htmlstring)
parsed_html = html.fromstring(str.decode('utf-8'))
# get inner text under comment node npm:article-content
inner_text_list = parsed_html.xpath("//founder-content/descendant::*/child::text()")
if inner_text_list:
inner_text = 'n'.join(inner_text_list)
else:
inner_text = ""
# get outer text under founder-content node
outer_text_list = parsed_html.xpath("//founder-content/child::text()")
# combine the outer text and inner text list
# it only have following combination:
# - outer_text_list[0] + inner_text + outer_text_list[1]
# - outer_text_list[0] + inner_text
# - inner_text
if not outer_text_list:
content = inner_text
if len(outer_text_list) == 1:
content = outer_text_list[0] + 'n' + inner_text
elif len(outer_text_list) == 2:
content = outer_text_list[0] + 'n' + inner_text + 'n' + outer_text_list[1]
else:
content = inner_text
return content
这段代码中同样通过xpath来获取页面中的新闻信息,通过我们手动进行firebug的分析,有效的新闻信息包含在如下格式的代码中:
<founder-content style="font-size: 14px; line-height: 25px;">
<!--<npm:article-content>-->
...
<!--</npm:article-content>-->
</founder-content>
这里的xpath语法有点意思,首先npm:article-content这个元素节点是comment node,不是普通的html element node,所以你在找到founder-content节点后,用getchildren()方法并不能定位到npm:article-content节点。怎么办?
我们分析到可能出现这么几种情况:
- 在
<founder-content>
和<!--<npm:article-content>-->
之间存在text node, 并且在<!--</npm:article-content>-->
和</founder>
间也存在text node
<founder-content>
<text node>
<!--<npm:article-content>-->
<text node>
<!--</npm:article-content>-->
<text node>
</founder-content>
-
只在
<founder-content>
和<!--<npm:article-content>-->
之间存在text node<founder-content> <text node> <!--<npm:article-content>--> <text node> <!--</npm:article-content>--> </found-content>
-
只在
<!--</npm:article-content>-->
和</founder-content>
间存在text node<founder-content> <!--<npm:article-content>--> <text node> <!--</npm:article-content>--> <text node> </founder-content>
我们做出如下定义:
- 在
<founder-content>
和<!--<npm:article-content>-->
节点间的text节点, 定义为outer_text_front - 在
<!--</npm:article-content>-->
和</founder-content>
节点间的text节点, 定义为outer_text_end - 在
<!--<npm:article-content>-->
和<!--</npm:article-content>-->
节点间的text节点,定义为inner_text
不论在哪种情况,inner_text中是存在的,如果我们解析出的outer_text_list的数目只有一个,那就说明这个outer_text只可能是outer_text_front, 如果我们解析出的outer_text_list的数目有两个,那么第一个就是outer_text_front, 第二个就是outer_text_end, 我们将outer_text与inner_text按特定顺序拼接起来就是完整的新闻内容。
outer_text_list我们可以通过获取<founder-content>
的直接子节点的text内容获取,inner_text_list我们可以通过获取<founder-content>
的最内层子节点的text内容获取,这里我们假设在<!--<npm:article-content>-->
和<!--</npm:article-content>-->
之间只有一层text node,没有更深层次的text node, inner_text_list总是获取最深层次的text node,如果只有一层的话,它直接获取的是<!--<npm:article-content>-->
和<!--</npm:article-content>-->
间的内容。
好了,加上前篇所列的两个函数,主程序可以写成如下:
if __name__ == '__main__':
init_robot()
index_page = fetch_url(get_absolute_url('index', date.today()))
sections = get_news_sections(index_page)
for section_title in sections:
print '新闻版块: %s' % section_title.encode('utf-8')
news = sections[section_title]
for news_title in news:
print 't新闻标题: %s' % news_title.encode('utf-8')
news_url = get_absolute_url('news', date.today(), news[news_title])
if news_url:
htmlstring = fetch_url(news_url)
if htmlstring:
print 'tt%s' % get_news_content(htmlstring).encode('utf-8')
这里注意每次我们抓取的HTML内容要decode成python内部处理的unicode格式,然后输出时又要encode成utf-8来显示。 这里之所以用utf-8是因为我的terminal和当前locale都配置成了utf-8的格式的,如果你的terminal是gbk的,请编码成相应的gbk的。 好了,到此为止,所有的代码都提供并且分析了,下面是输出的部分内容,大家有兴趣在自己机器上也可以跑一跑!