爬取《三体》,相关网页内容如下:
https://book.douban.com/subject/2567698/comments/hot?p=1
开始之前我们先明确一下爬虫的基本步骤:
- 抓取
- urllib内建模块 urllib.request
- requests第三方库(中小型)
- scrapy框架(大型)
- 解析
- BeautifulSoup库
- re模块
这次我们就用到了requests库,BeautifulSoup库以及re模块
由于要读取50条短评,而每一页有20条,用一个while循环解决,第一页是p=1,第二页是p=2,以此类推。
观察评分和评论部分的网页源代码如下:
<span class="comment-info">
<a href="https://www.douban.com/people/petercat/">Peter Cat</a>
<span class="user-stars allstar40 rating" title="推荐"></span>
<span>2014-08-05</span>
</span>
</h3>
<p class="comment-content">
<span class="short">大刘不是文笔平庸,是根本没有文笔和人物。三体之所以仍旧很好看,的确仰赖其在物理、历史、数学、哲学方面极其渊博的知识,和将这些知识串联起来的想象力。不过还是会觉得遗憾,没有心理建构小说,那种震撼永远是表层的。</span>
可以发现"user-stars allstar40 rating"里的40是评分,而<span class="short">中的内容是评论,评论很容易用BeautifulSoup解析得到,而评分用re模块进行解析。需要注意的是第三页全部读到列表里的话就会总共有60条,所以用count_del记录一下,最后再删掉多余的。上代码:
# -*- coding: utf-8 -*-
import requests
import re
from bs4 import BeautifulSoup
count=count_del=0
allstars=[]
i=1
while count<50:
try:
r=requests.get('https://book.douban.com/subject/2567698/comments/hot?p='+str(i))
except:
print('fail to crawl!')
break
soup=BeautifulSoup(r.text,'lxml')
comments=soup.find_all('span','short')
pattern=re.compile(r'<span class="user-stars allstar(.*?) rating"')
stars=pattern.findall(r.text)
allstars.extend(stars)
for item in comments:
if count<50:
print(count+1,item.string)
count+=1
else:
count_del+=1
i+=1
#有点问题,最后被截去的评论可能没有评分,就会把原本位于前50的评分删掉
remainStars=allstars[:-count_del]
print(len(remainStars),'-->',remainStars)
但是这时有个问题就是假如最后10条有的评论没评分,那么删去的10个评分中就会有位于前50的评分,这不是我们想要的。
改进
改进之处在于把没打分的一项单独识别并显示出来。观察网页源代码发现没打分的部分缺少了一行
打分的:
<a href="https://www.douban.com/people/bighead/">大头绿豆</a>
<span class="user-stars allstar50 rating" title="力荐"></span>
<span>2017-03-03</span>
没打分的:
<a href="https://www.douban.com/people/duanmuyi/">小波福娃</a>
<span>2013-03-21</span>
,而上下都是一样的,据此写出正则表达式,提取出来这一部分,根据这一部分的有无来设置分数的有无。尤其要注意的是上下行之间存在着\n和许多空格,所以正则表达式中的\s*很关键
也可以加参数re.S使得(.*?)匹配到\n,但是这样findall()会把没有打分的也匹配了一个字符串'\n ',这样search()方法就会匹配到None,不如让findall()匹配到空'',更好操作。
- 改进后的代码:
# -*- coding: utf-8 -*-
import requests
import re
from bs4 import BeautifulSoup
count=0
i=1
while count<50:
try:
r=requests.get('https://book.douban.com/subject/2567698/comments/hot?p='+str(i))
except:
print('fail to crawl!')
break
soup=BeautifulSoup(r.text,'lxml')
comments=soup.find_all('span','short')
pattern1=re.compile(r'<a href="https://www.douban.com/people.*?</a>\s*(.*?)\s*<span>.*?</span>')
pattern2=re.compile(r'<span class="user-stars allstar(.*?) rating" ')
stars=pattern1.findall(r.text)
for j in range(len(stars)):
if stars[j]:
stars[j]=int(pattern2.search(stars[j]).group(1))
else:
stars[j]=None
for j in range(len(comments)):
if count==50:
break
if stars[j]:
print(count+1,stars[j],comments[j].string)
else:
print(count+1,'No star',comments[j].string)
count+=1
i+=1
- 爬取结果:
...
16 40 毕竟还在用“就像小鸟眷恋着天空”这样的比喻,算了,都是文科生的矫情。
17 20 终于正式全部看完了,终于可以正式表示不好看。。。
18 40 星空如此浩瀚,人们却只能为蜗居浪费一生。活该被鄙视:你们是虫子。
19 20 对于这部几乎被誉为“当代中国最高科幻水准”的科幻大作,我却很难从中得到任何阅读的快感,反而是为了一探究竟(到底它牛逼在哪里)的心态支持我看完。虽然已经在看第二部,但目前还没发现有什么惊喜出现,且由于大量的物理名词出现,使得阅读感越来越差。
20 No star 不爱跟风所以拖到现在才读过传说中的三体,作为小说它的技巧叙述手法和人物塑造简直惨不忍睹(多年前第一次读刘慈欣到现在他的文学修养怎么一点长进都没有),但是故事的内核很饱满丰富,干货多,粗糙但不乏深刻,坚强一点的话可以无视文笔技巧挺过去读完。3.5星吧。
21 20 糟糕的人设和跳脱的情节硬是把一个绝佳的概念写得毫无趣味。
...
48 30 了得小姐推荐的,可是,看了一天加一晚,睡着过去三次,我是真的很认真努力了。可是先把理念架构放一边,作者写故事的能力实在欠佳。也可能是从小看书到大胃口已经逐渐刁钻,最介意的除了故事性和作者的思想,首先要求文风老辣。
49 No star 长途重新读了一遍。终于发现三体1确实还是写得比较糙的……以及从热门划线里,可以分析出现在读者的口味……
50 30 科幻足够硬,脑洞足够大,文笔足够俗,三观足够雷,奇葩,奇葩奇葩
可以发现第20个人(也就是第一页的最后一个人)没有打分。
注意加了re.S的区别,以及\s*匹配到'\n '的细节
>>> import re
>>> pattern=re.compile(r'<a href="https://www.douban.com/people.*?</a>\s*(.*?)\s*<span>.*?</span>')
>>> s='''
... </span>
<span class="comment-info">
<a href="https://www.douban.com/people/bighead/">大头绿豆</a>
<span class="user-stars allstar50 rating" title="力荐"></span>
<span>2017-03-03</span>
</span>
</h3>'''
... ... ... ... ... ... >>> pattern.findall(s)
['<span class="user-stars allstar50 rating" title="力荐"></span>']
>>> s
'\n</span>\n <span class="comment-info">\n <a href="https://www.douban.com/people/bighead/">大头绿豆</a>\n <span class="user-stars allstar50 rating" title="力荐"></span>\n <span>2017-03-03</span>\n </span>\n </h3>'
>>> pattern=re.compile(r'<a href="https://www.douban.com/people.*?</a>(.*?)<span>.*?</span>',re.S)
>>> pattern.findall(s)
['\n <span class="user-stars allstar50 rating" title="力荐"></span>\n ']
>>> re.findall(r'a.*?b','aba9b a \t b a\n b a\nb')
['ab', 'a9b', 'a \t b']
>>> re.findall(r'a.*?b','aba9b a \t b a\n b a\nb',re.S)
['ab', 'a9b', 'a \t b', 'a\n b', 'a\nb']