爬取豆瓣有关张国荣日记(二)—— 策略源码知识点

本篇介绍爬取豆瓣日记的策略分析、源码剖析、知识点汇总

(先放个封面图)


一代偶像 张国荣

</br>
本来想用Scrapy来爬的,结果连续被ban。
设置动态UA、加Cookies、用vpn也无济于事,辗转一天多,累觉不爱。

反爬机制不要太强啊,给豆瓣小组点个赞,跪服!!

9086036730690

</br>
不过,最后还是用一般方法的解决了
说来也奇怪,大概因为Scrapy是异步多线程,所以容易被发现吧。
</br>

一、目标

爬取豆瓣所有关于张国荣的日记
1、获取每一篇标题、作者、链接、点赞数、发布时间,将数据存入excel
2、获取所有日记内容,存入txt
3、将所有文章汇总,jieba分词,做成词云图
</br>

二、过程

1、分析网页及源码
豆瓣首页搜索框中输入 张国荣
回车页面跳转
以第一篇日记为例,点开
查看源码,分析得到URL
顺带分析下其它

得到具体日记的URL,接下来就是翻页


下拉看到显示更多
第2页 start=20
第3页start=40


发现规律了么,每一次刷新,URL=https://www.douban.com/j/search?q=张国荣&start=n&cat=1015, 其中n=0,20,40...那我们完全可以直接这样构造,每一次获取20项内容(为一个list),不用一个一个来了,手动试了下共有n最大为2000

</br>

2、具体步骤及问题解决
a、构造URL,解析json格式

代码这样,注意range函数:

for i in range(0,2001,20):
  response=self.get_source(url='https://www.douban.com/j/search?q=张国荣+&start='+str(i)+'&cat=1015')
  main=json.loads(response.text)['items']
  mains.append(main)

</br>

b、解析每一项获得id,由id构造每一篇链接

直接在源码上看的话,眼花,我是for循环打印到IDE中看的
用到BeautifulSoup和正则:

links=[]
#get_main()是上面解析数据的函数
#得到包含20项内容的大list
mains=self.get_main()
for main in mains:
    #每一页有20项
    for j in range(0,20):
        #先找到所有h3标签
        soup=BeautifulSoup(main[j],'lxml').find('h3')
        #在其中匹配id
        pattern=re.compile(r'<a href=.*?sid:(.*?)>(.*?)</a>', re.S)
        items=re.findall(pattern, str(soup))
        for item in items:
            id=item[0][0:-3].strip()
            link='https://www.douban.com/note/'+str(id)+'/'
            #所有链接放入list中
            links.append(link)
            time.sleep(0.2)

</br>
看样子是没错,然而,运行时却出问题。
List index out of range(抱歉忘记截图了)
搞了好久,终于发现了,比如start=120时:

也就是说,并不是“每一页”都有20项,有的少于20项。我以为120这里是个特殊,后面发现很多别的页面也有这样的状况。不能愉快地用range函数for循环了,好气呀。
还好本宝宝机智,想到itertools迭代器,可是超出索引范围会报IndexError。
想了下,加个try...except...,解决!

#记得导入itertools模块
import itertools
links=[]
mains=self.get_main()
for main in mains:
    try:
        for j in itertools.count(0):
            xxxxxxxx(和上面那一堆一样)
    except IndexError:
        continue                 

</br>

c、提取每一页中标题、作者、日期、内容、点赞数等信息

具体项目放入名为data的list中,多个data放入名为container的大list中

程序跑起来没有问题,可是打开点赞数那一栏,却是空的。跑去分析源码,发现原来没那么简单。如何入手解决,想了很久。

突然想到之前貌似看到过什么:


发现了么,原来豆瓣分pc端和移动端,移动端是m.douban.com形式(m 即为move),试了一下,这两个页面还是有些不一样的。

既然pc端获取不到点赞数,那么何不试试移动端。于是出现这样:


果然有差别,看到红框框里这个data-needlogin应该觉悟,这是需要登录才能看到点赞数呀(或者喜欢),于是模拟登录。试了才知道,post方法根本就行不通。

没辙了么,别忘了我们还有个简单的,用Cookies模拟登录也可以呀,再加个Session保持会话。

#这里用的首页的cookies
cookies='bid=6xgbcS6Pqds; ll="118318"; viewed="3112503"; gr_user_id=660f3409-0b65-4195-9d2f-a3c71573b40f; ct=y; ps=y; _ga=GA1.2.325764598.1467804810; _vwo_uuid_v2=112D0E7472DB37089F4E96B7F4E5913D|faf50f21ff006f877c92e097c4f2819c; ap=1; push_noty_num=0; push_doumail_num=0; _pk_ref.100001.8cb4=%5B%22%22%2C%22%22%2C1491576344%2C%22http%3A%2F%2Fwww.so.com%2Flink%3Furl%3Dhttp%253A%252F%252Fwww.douban.com%252F%26q%3D%25E8%25B1%2586%25E7%2593%25A3%26ts%3D1491459621%26t%3Df67ffeb4cd66c531150a172c69796e0%26src%3Dhaosou%22%5D; __utmt=1; _pk_id.100001.8cb4=41799262efd0b923.1467804804.35.1491576361.1491567557.; _pk_ses.100001.8cb4=*; __utma=30149280.325764598.1467804810.1491566154.1491576346.34; __utmb=30149280.3.10.1491576346; __utmc=30149280; __utmz=30149280.1491469694.24.15.utmcsr=baidu|utmccn=(organic)|utmcmd=organic; __utmv=30149280.12683; dbcl2="126831173:APSgA3NPab8"'
headers={'User_Agent':'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36','Cookie':cookies}
s=requests.Session()
response=s.get(url,headers=headers)
requests.adapters.DEFAULT_RETRIES=5

然后就是这样,加Cookies模拟登录后,用pc端的URL也可以(后面都抓的pc端)。


到了提取具体信息的环节。还需要要注意的是,“喜欢”那一栏可能没有数据

而且分析源码可以知道,有人点赞的有<span class="fav-num".?>(.?)</span>这一项,点赞为0的是没有这一项的。
陷阱真多-_-|| 分情况讨论匹配正则。

links=self.get_link()
container=[]
n=1
for link in links:
    html=self.get_source(link).text
    data=[]
    #得先判断有无点赞
    patternNum=re.compile(r'class="fav-num"',re.S)
    Num=re.search(patternNum,html)
    if Num:
        pattern=re.compile(r'<h1>(.*?)</h1>.*?<a href=.*?class="note-author">(.*?)</a>.*?<span class="pub-date">(.*?)</span>.*?<div class="note" id="link-report">(.*?)</div>.*?<span class="fav-num".*?>(.*?)</span>',re.S)
        items=re.findall(pattern,html)
        for item in items:
            data.append(item[0])
            data.append(link)
            data.append(item[1])
            data.append(item[2])
            data.append(item[4])
            data.append(self.tool.replace(item[3]))
    else:
        pattern=re.compile(r'<h1>(.*?)</h1>.*?<a href=.*?class="note-author">(.*?)</a>.*?<span class="pub-date">(.*?)</span>.*?<div class="note" id="link-report">(.*?)</div>',re.S)
        items=re.findall(pattern, html)
        for item in items:
            data.append(item[0])
            data.append(link)
            data.append(item[1])
            data.append(item[2])
            #无点赞时用0代替
            data.append('0')
            data.append(self.tool.replace(item[3]))
    container.append(data)
    time.sleep(0.5)
    n+=1

细心的盆友可能会叫住了,等等,这个self.tool.replace()怎么回事?

应该想到,豆瓣日记除了文字,还有部分可能是含图片甚至音频等
所以定义了一个Tool类,清洗多余的div、img、td等标签

class Tool():
    def replace(self,x):
        x=re.sub(re.compile('<br>|</br>||<p>|</p>|<td>|</td>|<tr>|</tr>|</a>|<table>|</table>'), "", x)
        x=re.sub(re.compile('<div.*?>|<img.*?>|<a.*?>|<td.*?>'), "", x)
        return x.strip()

class Spider():
    def __init__(self):
        self.tool=Tool()

</br>

d、保存数据入excel和txt

目前获得标题、作者、链接、发布时间、点赞数、文章内容共6项。
我们将前5项录入excel,最后一项存入txt。

可以先将文章录入txt,接着list中去除文章一项, 最后录入excel。用到remove方法。
一定警惕list=list.remove(i)这种写法!
为什么呢,看下面:

list=list.remove(i)其实是默认返回None,然后。。。就是个大坑啊,基础不牢地动山摇论小白学习的心酸血泪史≧﹏≦

e、结巴分词及词云图

这回共2000多篇文章,字数太多不可能再用语料库在线统计词频,所以采用统计权重的方法,再乘以10000放大。
这里参考了博客:http://www.jianshu.com/p/6a285dfa3d87
取了权重最大的前150个词(后面作词云时又相应去掉了一些)

import jieba.analyse
f=open(r'F:\Desktop\DouBan.txt','r')
content=f.read()
try:
    jieba.analyse.set_stop_words(r'F:\Desktop\TingYong.txt')
    tags=jieba.analyse.extract_tags(content,topK=150, withWeight=True)
    for item in tags:
        print item[0]+'\t'+str(int(item[1]*10000))
finally:
    f.close()

TingYong.txt是停用词表,可以在网上下载到,还可以自己修改添加。
词云就差不多和之前一样,可见文章 http://www.jianshu.com/p/462d32450b5f
</br>

三、代码

完整版代码我放github上了:https://github.com/LUCY78765580/Python-web-scraping/tree/master/DouBan
(如果您觉得有用,star我也可以的哟~)
豆瓣反爬比较厉害,半夜抓取可能比较好。
</br>

四、总结

最后总结一下本文关键
1、源码分析:首先分析网站及URL结构;发现豆瓣不仅有pc端还有移动端,而且两者页面不太一样;分析出不登录的话是获取不到点赞数的,同时有无点赞页面代码是不一样的。
2、抓包获取URL,解析json格式数据
3、Cookies模拟登录,Session保持会话
4、数据提取与清洗:用到正则re、BeautifulSoup及自定义的Tool类
5、用jieba.analyse编制程序,结巴分词、词频统计词云制作
6、基础知识,range(start,end,n)、itertools.count(i)、remove等方法

陷阱与知识点的大杂烩

用来作为入门阶段的复习总结倒是不错的。本篇就是这样啦~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,577评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,103评论 18 139
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,626评论 4 59
  • 冬至,梦到自己是条鱼 就在前天下午,好友智实短信提醒我要吃冬至饺子,我才知道第二天便是...
    尔峰阅读 433评论 4 5
  • 当你自己不对自己敷衍的时候,别人自然不敢对你敷衍。 那你就活成了自己想要的自己!
    袁益君阅读 278评论 0 0