用Scrapy抓取豆瓣电影Top250排行

scrapy主代码:

# 抓取主代码
import scrapy
import re
from douban.items import DoubanTopItem

class MoviesInfo(scrapy.Spider):

    def __init__(self):
        self.start = 0

    name = "movies_info"
    allowed_domains = ["douban.com"]
    start_urls = [
        'https://movie.douban.com/top250?start=0', # 豆瓣top250
    ]

    def parse(self, response):
        item = DoubanTopItem()
        res_infos = response.css('div.info')

        for res in res_infos:
            tmp = {}
            hd = res.css('div.hd') # 电影名称
            movie_title_list = hd.css('span::text').extract()
            # "".join(m.split()) 去除\xa0(也就是 )
            item['title'] = ''.join(''.join(m.split()) for m in movie_title_list[:-1]) 
            bd = res.css('div.bd') # 电影评分和评分人数
            score_and_count = bd.css('div.star span::text').extract()
            score = score_and_count[0]
            count = score_and_count[1]
            item['score'] = float(score)
            regex = re.compile('^[0-9]*')
            count_str = regex.findall(count)[0] # 取出数字字符串
            item['count'] = int(count_str)
            yield item

        self.start += 25
        while self.start <= 225:
            next_page = 'https://movie.douban.com/top250?start=' + str(self.start)
            yield scrapy.Request(next_page, callback=self.parse)
            
# 将结果写入markdown
class FilePipeline:

    def __init__(self):
        self.id = 1

    def process_item(self, item, spider):
        with open('top250.md', 'a+', encoding='utf-8') as f:
            title = item.get('title')
            score = item.get('score')
            count = item.get('count')
            f.write('|' + str(self.id) + '|' + title.split('/')[0] + '|' + 
                str(score) + '|' + str(count) + '|\n')
            self.id += 1
        return item     

2017.12.12更新代码:

import scrapy
import urllib
import re
import time
from random import choice
from scrapy.spiders import Spider
from scrapy.http import FormRequest
from douban.items import DoubanMovieTop250Item


class DoubanGetInfoNoDetail(Spider):

    def __init__(self, username, nickname, password):
        self.top_num = 0
        self.username = username
        self.nickname = nickname
        self.password = password

    name = 'douban_get_info_no_detail'

    start_urls = ['https://movie.douban.com/top250?start=' + str(i)
                    for i in range(250) if i % 25 == 0]

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            username = crawler.settings.get('DOUBAN_USERNAME'),
            nickname = crawler.settings.get('DOUBAN_NICKNAME'),
            password = crawler.settings.get('DOUBAN_PASSWORD')
        )

    def parse(self, response):
        self.logger.warning("进入parse处理")
        res_infos = response.css('div.info')
        for res in res_infos:
            item = DoubanMovieTop250Item()
            hd = res.css('div.hd') # 电影名称
            movie_title_list = hd.css('span::text').extract()
            # "".join(m.split()) 去除\xa0(也就是&nbsp;)
            item['full_name'] = ''.join("".join(m.split()) for m in movie_title_list[:-1])
            item['name'] = item['full_name'].split('/')[0]
            bd = res.css('div.bd') # 电影评分和评分人数
            score_and_count = bd.css('div.star span::text').extract()
            score = score_and_count[0]
            count = score_and_count[1]
            item['score'] = float(score)
            regex = re.compile('^[0-9]*')
            count_str = regex.findall(count)[0] # 取出数字字符串
            item['count'] = int(count_str)
            self.top_num += 1
            item['top_num'] = self.top_num
            item['movie_intro'] = ''
            item['detail_url'] = ''
            yield item

附加详情页的代码:

import scrapy
import urllib
import re
import time
from random import choice
from scrapy.spiders import Spider
from scrapy.http import FormRequest
from douban.items import DoubanMovieTop250Item, DoubanMovieDetail


class DoubanGetInfo(Spider):

    def __init__(self, username, nickname, password):
        self.top_num = 0
        self.username = username
        self.nickname = nickname
        self.password = password

    name = 'douban_get_info'

    start_urls = ['https://movie.douban.com/top250?start=' + str(i)
                    for i in range(250) if i % 25 == 0]

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            username = crawler.settings.get('DOUBAN_USERNAME'),
            nickname = crawler.settings.get('DOUBAN_NICKNAME'),
            password = crawler.settings.get('DOUBAN_PASSWORD')
        )

    def parse(self, response):
        self.logger.warning("进入parse处理")

        res_infos = response.css('div.info')
        for res in res_infos:
            item = {}
            tmp = {}
            # 电影详情页的url
            url_con_str = res.css('a').extract_first()
            url_re = re.compile('"(.*?)"')
            url = url_re.findall(url_con_str)
            item['detail_url'] = url[0]
            hd = res.css('div.hd') # 电影名称
            movie_title_list = hd.css('span::text').extract()
            # "".join(m.split()) 去除\xa0(也就是&nbsp;)
            item['full_name'] = ''.join("".join(m.split()) for m in movie_title_list[:-1])
            item['name'] = item['full_name'].split('/')[0]
            bd = res.css('div.bd') # 电影评分和评分人数
            score_and_count = bd.css('div.star span::text').extract()
            score = score_and_count[0]
            count = score_and_count[1]
            item['score'] = float(score)
            regex = re.compile('^[0-9]*')
            count_str = regex.findall(count)[0] # 取出数字字符串
            item['count'] = int(count_str)
            self.top_num += 1
            item['top_num'] = self.top_num
            if item['detail_url']:
                yield scrapy.Request(
                    item['detail_url'],
                    callback=self.parse_movie_detail,
                    meta={'item': item},
                    dont_filter=True
                    )

    def parse_movie_detail(self, response):
        self.logger.warning("进入parse_movie_detail处理")
        item = DoubanMovieTop250Item()
        d = response.meta['item']

        movie_intro = ''
        if response.status == 200:
            x_str = '//*[@id="link-report"]//span[@property="v:summary"]/text()'
            movie_intro = response.xpath(x_str).extract_first()
            if movie_intro:
                movie_intro = movie_intro.strip().replace('"', '\"').replace("'", "\'")
        else:
            self.logger.error(response.url)
            self.logger.error('parse_movie_detail 状态码为:{}'.format(response.status))

        d['movie_intro'] = movie_intro
        item = d
        self.logger.warning(item)
        yield item

附加登录的代码:

import scrapy
import urllib
import re
import time
from random import choice
from scrapy.spiders import Spider
from scrapy.http import FormRequest
from douban.items import DoubanMovieTop250Item, DoubanMovieDetail


class DoubanLoginInfo(Spider):

    def __init__(self, username, nickname, password):
        self.top_num = 0
        self.username = username
        self.nickname = nickname
        self.password = password

    name = 'douban_login_and_get_info'

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            username = crawler.settings.get('DOUBAN_USERNAME'),
            nickname = crawler.settings.get('DOUBAN_NICKNAME'),
            password = crawler.settings.get('DOUBAN_PASSWORD')
        )

    def start_requests(self):
        self.logger.warning("start_request 请求入口")
        login_url = 'https://www.douban.com/accounts/login'
        return [scrapy.Request(login_url, meta={'cookiejar': 1}, callback=self.post_login)]

    def post_login(self, response):
        self.logger.warning("提交参数模拟登录")
        html = urllib.request.urlopen(response.url).read().decode('utf-8')
        # 验证码图片地址
        search_str = '<img id="captcha_image" src="(.+?)" alt="captcha" class="captcha_image"/>'
        imgurl = re.search(search_str, html)
        if imgurl:
            url = imgurl.group(1)
            # 将图片保存至同目录下
            res = urllib.request.urlretrieve(url, 'v.jpg')
            # 获取captcha-id参数
            search_str2 = '<input type="hidden" name="captcha-id" value="(.+?)"/>'
            captcha = re.search(search_str2, html)
            if captcha:
                vcode = input('请输入图片上的验证码:')
                return [FormRequest.from_response(response,
                          meta={'cookiejar': response.meta['cookiejar']},
                          formdata={
                              'source': 'index_nav',
                              'form_email': self.username,
                              'form_password': self.password,
                              'captcha-solution': vcode,
                              'captcha-id': captcha.group(1)
                          },
                          callback=self.after_login,
                          dont_filter=True)
                        ]
        return [FormRequest.from_response(response,
                  meta={'cookiejar': response.meta['cookiejar']},
                  formdata={
                      'source': 'index_nav',
                      'form_email': self.username,
                      'form_password': self.password
                  },
                  callback=self.after_login,
                  dont_filter=True)
                ]


    def after_login(self, response):
        self.logger.warning("模拟登录结束")
        urls = ['https://movie.douban.com/top250?start=' + str(i)
                    for i in range(250) if i % 25 == 0]
        for url in urls:
            time.sleep(1)
            yield scrapy.Request(url, meta={'cookiejar': response.meta['cookiejar']})


    def parse(self, response):
        self.logger.warning("进入parse处理")

        # unicode_body = response.body_as_unicode()
        # if self.nickname in unicode_body:
        #     self.logger.warning("模拟登录成功")
        # else:
        #     self.logger.warning("模拟登录失败")

        res_infos = response.css('div.info')
        for res in res_infos:
            item = {}
            tmp = {}
            # 电影详情页的url
            url_con_str = res.css('a').extract_first()
            url_re = re.compile('"(.*?)"')
            url = url_re.findall(url_con_str)
            item['detail_url'] = url[0]
            hd = res.css('div.hd') # 电影名称
            movie_title_list = hd.css('span::text').extract()
            # "".join(m.split()) 去除\xa0(也就是&nbsp;)
            item['full_name'] = ''.join("".join(m.split()) for m in movie_title_list[:-1])
            item['name'] = item['full_name'].split('/')[0]
            bd = res.css('div.bd') # 电影评分和评分人数
            score_and_count = bd.css('div.star span::text').extract()
            score = score_and_count[0]
            count = score_and_count[1]
            item['score'] = float(score)
            regex = re.compile('^[0-9]*')
            count_str = regex.findall(count)[0] # 取出数字字符串
            item['count'] = int(count_str)
            self.top_num += 1
            item['top_num'] = self.top_num
            time.sleep(choice(range(3)))
            yield scrapy.Request(
                item['detail_url'],
                callback=self.parse_movie_detail,
                meta={'item': item, 'cookiejar': response.meta['cookiejar']},
                dont_filter=True
            )


    def parse_movie_detail(self, response):
        self.logger.warning("进入parse_movie_detail处理")
        item = DoubanMovieTop250Item()
        d = response.meta['item']

        movie_intro = ''
        if response.status == 200:
            x_str = '//*[@id="link-report"]//span[@property="v:summary"]/text()'
            movie_intro = response.xpath(x_str).extract_first()
            if movie_intro:
                movie_intro = movie_intro.strip().replace('"', '\"').replace("'", "\'")
        else:
            self.logger.error(response.url)
            self.logger.error('parse_movie_detail 状态码为:{}'.format(response.status))

        d['movie_intro'] = movie_intro
        item = d
        self.logger.warning(item)
        yield item

结果如下,豆瓣top250的排序算法并不是简单的计算评分和评分人数,而是用一种综合的排序算法

排名 电影名 评分 评分人数
1 肖申克的救赎 9.6 917921
2 霸王别姬 9.5 662297
3 这个杀手不太冷 9.4 871714
4 阿甘正传 9.4 741643
5 美丽人生 9.5 432994
6 千与千寻 9.2 691880
7 辛德勒的名单 9.4 397953
8 泰坦尼克号 9.2 681863
9 盗梦空间 9.3 790845
10 机器人总动员 9.3 516662
11 海上钢琴师 9.2 618186
12 三傻大闹宝莱坞 9.1 700357
13 忠犬八公的故事 9.2 476508
14 放牛班的春天 9.2 470611
15 大话西游之大圣娶亲 9.2 508881
16 教父 9.2 356590
17 龙猫 9.1 434269
18 楚门的世界 9.1 477106
19 乱世佳人 9.2 279806
20 熔炉 9.2 275535
21 天堂电影院 9.1 318889
22 触不可及 9.1 379917
23 当幸福来敲门 8.9 558406
24 无间道 9.0 416178
25 星际穿越 9.1 505230
26 十二怒汉 9.4 177829
27 搏击俱乐部 9.0 414340
28 怦然心动 8.9 571366
29 指环王3:王者无敌 9.1 293516
30 少年派的奇幻漂流 9.0 596488
31 鬼子来了 9.2 238023
32 天空之城 9.0 343687
33 蝙蝠侠:黑暗骑士 9.0 334512
34 活着 9.1 278372
35 罗马假日 8.9 397195
36 大话西游之月光宝盒 8.9 410764
37 飞屋环游记 8.9 512003
38 两杆大烟枪 9.0 280532
39 窃听风暴 9.1 230217
40 飞越疯人院 9.0 284605
41 海豚湾 9.3 185681
42 闻香识女人 8.9 330817
43 V字仇杀队 8.8 428489
44 控方证人 9.6 94380
45 哈尔的移动城堡 8.9 359524
46 教父2 9.1 191890
47 美丽心灵 8.9 311949
48 死亡诗社 8.9 285591
49 指环王2:双塔奇兵 8.9 276714
50 指环王1:魔戒再现 8.9 309437
51 辩护人 9.1 211584
52 情书 8.8 384170
53 疯狂动物城 9.2 512226
54 美国往事 9.1 179095
55 饮食男女 9.1 203861
56 天使爱美丽 8.7 491128
57 钢琴家 9.0 206819
58 狮子王 8.9 287197
59 七宗罪 8.7 443280
60 被嫌弃的松子的一生 8.9 290905
61 致命魔术 8.8 350602
62 小鞋子 9.2 139974
63 勇敢的心 8.8 299554
64 剪刀手爱德华 8.7 491709
65 音乐之声 8.9 235417
66 素媛 9.1 184255
67 低俗小说 8.8 340274
68 本杰明·巴顿奇事 8.7 399737
69 黑客帝国 8.8 285511
70 沉默的羔羊 8.7 358494
71 入殓师 8.8 322538
72 拯救大兵瑞恩 8.9 241324
73 西西里的美丽传说 8.7 353893
74 蝴蝶效应 8.7 386793
75 玛丽和马克思 8.9 234455
76 春光乍泄 8.8 249437
77 让子弹飞 8.7 629840
78 心灵捕手 8.7 273988
79 大闹天宫 9.2 100076
80 幽灵公主 8.8 237210
81 阳光灿烂的日子 8.7 273092
82 第六感 8.8 237390
83 重庆森林 8.7 333493
84 大鱼 8.7 254869
85 射雕英雄传之东成西就 8.7 278488
86 禁闭岛 8.6 362782
87 甜蜜蜜 8.8 234600
88 阳光姐妹淘 8.8 270511
89 一一 8.9 157403
90 狩猎 9.0 129399
91 致命ID 8.6 321100
92 上帝之城 8.9 159268
93 末代皇帝 8.9 166962
94 告白 8.7 345121
95 看不见的客人 8.7 277410
96 布达佩斯大饭店 8.7 311302
97 断背山 8.6 320282
98 加勒比海盗 8.6 345627
99 猫鼠游戏 8.7 225259
100 哈利·波特与魔法石 8.7 254863
101 摩登时代 9.2 88835
102 爱在黎明破晓前 8.7 221335
103 阿凡达 8.6 530809
104 风之谷 8.8 176673
105 爱在日落黄昏时 8.7 199227
106 穿条纹睡衣的男孩 8.9 144638
107 侧耳倾听 8.8 168385
108 消失的爱人 8.7 355302
109 萤火虫之墓 8.7 203925
110 超脱 8.8 172750
111 驯龙高手 8.7 284471
112 倩女幽魂 8.6 249750
113 恐怖直播 8.7 219034
114 菊次郎的夏天 8.7 181604
115 红辣椒 8.9 133425
116 幸福终点站 8.6 225096
117 海洋 9.0 99087
118 七武士 9.2 77254
119 岁月神偷 8.6 307209
120 神偷奶爸 8.5 374250
121 借东西的小人阿莉埃蒂 8.7 215816
122 电锯惊魂 8.7 198556
123 谍影重重3 8.7 180295
124 杀人回忆 8.6 209457
125 贫民窟的百万富翁 8.5 395490
126 真爱至上 8.5 307314
127 雨人 8.6 209650
128 燃情岁月 8.7 148718
129 东邪西毒 8.6 248951
130 记忆碎片 8.5 278637
131 小森林夏秋篇 8.9 127859
132 喜宴 8.8 138928
133 喜剧之王 8.5 310302
134 虎口脱险 8.9 106893
135 怪兽电力公司 8.6 238042
136 疯狂原始人 8.7 368331
137 黑天鹅 8.5 415127
138 卢旺达饭店 8.8 118759
139 英雄本色 8.7 159531
140 猜火车 8.5 247758
141 穿越时空的少女 8.6 184599
142 恋恋笔记本 8.5 279594
143 魂断蓝桥 8.8 128620
144 雨中曲 8.9 92616
145 傲慢与偏见 8.4 298827
146 教父3 8.7 128691
147 完美的世界 9.0 79062
148 纵横四海 8.7 143336
149 萤火之森 8.7 154799
150 玩具总动员3 8.8 190258
151 7号房的礼物 8.7 172463
152 哈利·波特与死亡圣器(下) 8.6 270337
153 花样年华 8.5 244543
154 海边的曼彻斯特 8.6 168536
155 荒蛮故事 8.7 134232
156 二十二 8.8 84396
157 我是山姆 8.8 102061
158 人工智能 8.6 188074
159 浪潮 8.7 130968
160 香水 8.4 273368
161 小森林冬春篇 8.9 110034
162 冰川时代 8.4 267098
163 朗读者 8.5 275954
164 追随 8.9 81074
165 时空恋旅人 8.6 195315
166 撞车 8.6 172775
167 唐伯虎点秋香 8.4 317780
168 心迷宫 8.6 157838
169 罗生门 8.7 123123
170 超能陆战队 8.6 361565
171 一次别离 8.7 130646
172 蝙蝠侠:黑暗骑士崛起 8.5 290876
173 战争之王 8.5 172730
174 可可西里 8.7 127284
175 梦之安魂曲 8.7 110423
176 未麻的部屋 8.8 94719
177 地球上的星星 8.8 82879
178 爆裂鼓手 8.6 232902
179 碧海蓝天 8.7 107119
180 恐怖游轮 8.3 315844
181 秒速5厘米 8.3 290066
182 阿飞正传 8.5 177894
183 达拉斯买家俱乐部 8.6 164993
184 牯岭街少年杀人事件 8.7 104726
185 谍影重重2 8.5 156698
186 谍影重重 8.5 188923
187 海盗电台 8.6 163551
188 惊魂记 8.9 81160
189 被解救的姜戈 8.5 255194
190 魔女宅急便 8.4 190312
191 再次出发之纽约遇见你 8.5 164016
192 东京物语 9.2 49448
193 绿里奇迹 8.7 104619
194 青蛇 8.4 216092
195 末路狂花 8.7 102574
196 勇闯夺命岛 8.5 139687
197 哪吒闹海 8.8 78090
198 迁徙的鸟 9.1 51207
199 忠犬八公物语 9.0 56110
200 荒野生存 8.6 124838
201 终结者2:审判日 8.5 138314
202 卡萨布兰卡 8.6 122382
203 这个男人来自地球 8.5 175342
204 源代码 8.3 401270
205 变脸 8.4 210264
206 新龙门客栈 8.4 178999
207 燕尾蝶 8.6 106557
208 黑客帝国3:矩阵革命 8.5 151903
209 黄金三镖客 9.1 50232
210 E.T.外星人 8.5 149442
211 英国病人 8.4 171462
212 城市之光 9.2 42529
213 发条橙 8.4 183066
214 美国丽人 8.4 181838
215 无耻混蛋 8.4 219016
216 叫我第一名 8.6 106674
217 模仿游戏 8.5 247823
218 穆赫兰道 8.3 230100
219 非常嫌疑犯 8.6 113832
220 初恋这件小事 8.3 448543
221 勇士 8.9 86010
222 上帝也疯狂 8.6 94219
223 房间 8.8 144850
224 头脑特工队 8.7 216349
225 爱在午夜降临前 8.8 112043
226 蓝色大门 8.3 268653
227 无敌破坏王 8.6 180242
228 爱·回家 9.0 46339
229 血钻 8.5 127549
230 疯狂的石头 8.2 304124
231 国王的演讲 8.3 331001
232 大卫·戈尔的一生 8.6 89209
233 枪火 8.6 97282
234 麦兜故事 8.5 124728
235 千钧一发 8.7 82796
236 暖暖内含光 8.4 141289
237 曾经 8.3 198973
238 遗愿清单 8.5 123454
239 荒岛余生 8.5 129463
240 步履不停 8.8 65412
241 一个叫欧维的男人决定去死 8.7 107018
242 彗星来的那一夜 8.4 167051
243 蝴蝶 8.6 93544
244 我爱你 9.0 54629
245 巴黎淘气帮 8.6 102794
246 月球 8.5 139947
247 与狼共舞 8.9 54908
248 偷拐抢骗 8.5 113664
249 无人知晓 9.0 49374
250 寿司之神 8.8 80264