Python爬虫学习(5): 简单的爬取
学习了urllib,urlib2以及正则表达式之后就可以做一些简单的抓取以及处理工作。为了抓取方便,这里选择糗事百科的网页作为抓取对象。
1. 获取数据:
In [293]: url = "http://www.qiushibaike.com/hot" # 如果不加入用户代理会报错 In [294]: headers = {"User-Agent":"Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"} In [295]: request = urllib2.Request(url,headers=headers) In [296]: response = urllib2.urlopen(request) In [297]: content = response.read() In [298]: print content <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="X-UA-Compatible" content="chrome=1,IE=edge"> <meta name="renderer" content="webkit"/> <meta name="applicable-device" content="pc"> <title>
2. 抓取段子的文字内容:
我用的firefox浏览器,按F12,然后进入查看器,就可以对生成的页面代码进行查看,当点击某一项的时候会在网页中标注这一段代码会对应页面中哪一块。
段子都在id="content-left"的div之中,而其中的每一个子div都是一条段子。
我们展开其中的一条段子,这里将每一个段子区域划分为好几块:
其中class为contentHerf的<a>中有段子的内容,而我们要抓取就是这个中文内容。
2.1 获取段子文本内容
更具html结构我们写一个简单的获取段子文本内容的正则表达式:(?:<div.*?class=\"content\">)\s+.*?<span>(.*)<\/span>\s+.*?</div>
(?:<div.*class=\"content\">): 表示匹配分组中内容,但不记入匹配结果。上述表达式的蓝色部分是最终获取的结果
In [313]: pattern = re.compile(r"(?:<div.*class=\"content\">)\s+.*?<span>(.*)<\/span>\s+.*?</div>") # 查找所有匹配结果 In [314]: result = re.findall(pattern,content) In [315]: print result[0] 跟老婆正在吵架,老丈人碰巧来我家,三人面面相觑,场面十分尴尬,我正不知道该怎么说话,我老婆手机突然响了,那货直接按了免提,竟然是我岳母打来的,琴子,你爸有没有去你家?我刚跟他吵了一架,老家伙没吵赢,离家出走了!<br/>玛德,更尴尬了!!
\s+.*?的写法有点繁琐,这里在compile函数中加上r.S标志,将换行符号也纳入符号"."中,变换之后的写法如下:
pattern = re.compile(r"(?:<div.*?class=\"content\">).*?<span>(.*?)<\/span>.*?</div>",re.S)
在段子文本中有用<br/>表示的换行,需要将其替换为\n。这时候可以用sub函数。
In [319]: for item in result: ...: item = re.sub(re.compile(r"<br/>"),r"\n",item) ...: print item 跟老婆正在吵架,老丈人碰巧来我家,三人面面相觑,场面十分尴尬,我正不知道该怎么说话,我老婆手机突然响了,那货直接按了免提,竟然是我岳母打来的,琴子,你爸有没有去你家?我刚跟他吵了一架,老家伙没吵赢,离家出走了! 玛德,更尴尬了!!
..................
2.2 单独获取获取用户名:
In [354]: pattern = re.compile(r"(?:qiushi_tag_\d+).*?<h2>(.*?)</h2>",re.S) In [355]: result = re.findall(pattern,content) In [356]: len(result) Out[356]: 20 In [357]: print result[0] 匪徒~入库君
2.3 同时获取用户名,段子文本内容,以及点赞数量和评论数量:
In [384]: pattern = re.compile(r"(?:qiushi_tag_\d+).*?<h2>(.*?)</h2>.*?(?:contentHerf).*?<span>(.*?)<\/span>.*?(?:class=\"stats\").*?<i.*?>(\d+) ...: </i>.*?<i.*?>(\d+)</i>",re.S) In [385]: result = re.findall(pattern,content) In [386]: for item in result: ...: print "" ...: print "用户:",item[0] ...: print "段子内容:\n",item[1] ...: print "点赞数量:",item[2] ...: print "评论数量:",item[3]
运行结果结果:
2.4 获取带图片的段子中的图片地址
不是每一个段子都会有图片。图片的的地址一般包含在class="thumb"的div块中
In [417]: pattern = re.compile(r"(?:class=\"thumb\").*?<img.*?src=\"(.*?)\"",re.S) In [418]: result = re.findall(pattern,content) In [419]:
# 此次抓取中只有五个有图片的段子 Out[419]: ['http://pic.qiushibaike.com/system/pictures/11778/117782282/medium/app117782282.jpg', 'http://pic.qiushibaike.com/system/pictures/11778/117783971/medium/app117783971.jpg', 'http://pic.qiushibaike.com/system/pictures/11778/117784294/medium/app117784294.jpg', 'http://pic.qiushibaike.com/system/pictures/11778/117782437/medium/app117782437.jpg', 'http://pic.qiushibaike.com/system/pictures/11778/117783530/medium/app117783530.jpg']
2.5 同时获取用户名,段子文本内容,以及图片地址,以及点赞数量和评论数量
由于不是每一个段子都会有图片,所以在抓取的时候有的则抓,有图片地址的时候才抓取地址。下边绿色的部分是抓取图片地址的主代码。
In [470]: pattern = re.compile(r"(?:qiushi_tag_\d+).*?<h2>(.*?)</h2>.*?(?:contentHerf).*?<span>(.*?)<\/span>.*?(?:class=\"thumb\".*?<img.*?src=\ "(.*?)\"(?:.*?))?(?:class=\"stats\").*?<i.*?>(\d+)</i>.*?<i.*?>(\d+)</i>",re.S) In [471]: result = re.findall(pattern,content) In [472]: for item in result: ...: print "" ...: print "用户:",item[0] ...: print "段子内容:\n",item[1] ...: print "段子中图片地址:",item[2] ...: print "点赞数量:",item[3] ...: print "评论数量:",item[4]
2.6 比较完全的抓取
下边的正则表达式子中绿色字样可以看着是不同字段的分割符号,特别标注部分就是对应获取字段的内容的部分。
In [522]: pattern = re.compile(r"(?:qiushi_tag_(\d+)).*?href=\"/users/(\d+)/\".*?img.*?src=\"(.*?)\".*?<h2>(.*?)</h2>.*?(?:contentHerf).*?<span> ...: (.*?)<\/span>.*?(?:class=\"thumb\".*?img.*?src=\"(.*?)\"(?:.*?))?(?:class=\"stats\").*?<i.*?>(\d+)</i>.*?<i.*?>(\d+)</i>",re.S) In [523]: result = re.findall(pattern,content) In [525]: for item in result: ...: print "" ...: print "段子ID:",item[0] ...: print "用户ID:",item[1] ...: print "用户头像:",item[2] ...: print "用户名:",item[3] ...: print "段子内容:\n",item[4] ...: print "段子中图片地址:",item[5] ...: print "点赞数量:",item[6] ...: print "评论数量:",item[7]