手把手教你爬取豆瓣所有的电影(详细注释)

首先呢,命令行切换目录到桌面建立一个新scrapy项目

(不要问qwer是什么,起名就是这么随意)

>cd desktop
>scrapy startproject qwer   

简述一下大致整个过程:

首先输入scrapy crawl qwer启动爬虫文件,

然后爬虫文件会发送start_urls里的请求,会获取相应response,

接着response会传到parse函数中进行处理提取所需数据,

之后提取的数据会存到item对象里,item对象通过yield返回到管道文件,

最后管道进行处理并储存每个item对象,处理完后返回item,并准备接受下一个

第一步:定义items文件

import scrapy
class QwerItem(scrapy.Item):  #按照格式来就行
    # name = scrapy.Field()
    产地 = scrapy.Field()
    类型 = scrapy.Field()
    评分 = scrapy.Field()
    名字 = scrapy.Field()
    评分人数 = scrapy.Field()
    …………

第二步:爬虫主程序

在spiders文件夹下新建一个.py文件,这些是我导入的库

import scrapy
import json
import requests
import re
from qwer.items import QwerItem

定义出 爬虫的类

class QwerSpider(scrapy.Spider):
#先初始化
	name = 'qwer' 
	###可选allowed_domains = ['douban.com']
#起始页面用的是我不是药神
	start_urls = ['https://movie.douban.com/subject/26752088/'] 
#这段代码产出第一部电影的详细信息
	def parse(self,response): 
		sites = response.xpath('//div[@id="content"]') 
		for i in sites:
                        #创建item对象,用来储存数据
			item = QwerItem() 
			item['名字'] = i.xpath('h1/span[1]/text()').extract()
			item['类型'] = i.xpath('//*[@id="info"]/span[@property="v:genre"]/text()').extract()
                         ………………
			#返回给管道文件处理  然后回来继续下个循环
			yield item 

在正常爬取数据时发现有些信息发现有些信息如产地,语言,无法精确定位。所以我的思路是,先用text()爬取上一级目录下所有无法精确定位的内容(即引号内数据),然后通过正则表达式找出所需信息

手把手教你爬取豆瓣所有的电影(详细注释)

#这段代码读取产地等信息
                chan_di = i.xpath('//*[@id="info"]/text()').extract()
		x = re.compile(r'[u4e00-u9fa5]')   #引入正则表达式搜寻汉字 
		for k in range(len(chan_di)):
			data = re.findall(x, chan_di[k]) 
			if data: #只要找到第一组汉字就退出
				y = chan_di[k]    #因为取得的第一组汉字就是产地
				break
		item['产地']= y   #写入产地信息

翻页程序,因为豆瓣采用的动态加载翻页的,所以需要先进入网页开发模式,在Network下面选择XHR,然后刷新页面,如图所示的URL就是其正在的地址,每次点击“加载更多”后都会产生一个新地址,与第一个相比,只有start=后面的数字是变化的,按照每页20部电影的速度变化着

手把手教你爬取豆瓣所有的电影(详细注释)

写一段for循环,循环次数可以自定义(看你想爬多少电影了)

然后这里我是通过python的requests库得到每个页面上20部电影的详细地址

#接在parse函数下面,这段代码产出每一部电影的具体网址
		for m in range(500):
			url_0 = 'https://movie.douban.com/j/new_search_subjects?sort=T&range=0,10&tags=&start={}'.format(m*20)
			#这里返回的是 json文件所以要用 .json()
                        datas = requests.get(url_0).json() 
			dict_0 = datas['data']     #datas是一个字典,里面的data键里包含了所需要的全部信息
			for n in dict_0[1:]:       #去掉上面已经爬取的我不是药神
				z = n['url']       #读取每个页面上的每一部电影的网址
				zz = z.replace('','')    #读取出的原始网址中包含许多反斜杠,去掉
				yield scrapy.Request(zz,callback = self.parse_next)      #打开每一部电影的网址爬取信息

然后为了不进行无限循环,再重新定义一个parse_next函数(和原parse相同)来产出数据

#这段代码产出每部电影的相关信息
	def parse_next(self,response): 
         …………
         yield item         
#代码与上面parse函数相同,不过到yield item 就结束,不再需要下面的for循环

第三步定义管道文件

负责来处理item字段,最后储存成json文件

class QwerPipeline(object):

    def __init__(self):
        self.f = open('dianyssing_shuju.json',"w")  #打开一个json文件

    def process_item(self, item, spider):
        #要先把item转行成字典,其中ascii设置为否则中文可以按Unicode处理
        content = json.dumps(dict(item),ensure_ascii = False)+",n" #返回的是字符串,输出完一部后换行        
        self.f.write(content)  #写入  
        return item     #表明这条item数据处理完了

    def close_spider(self,spider):
        self.f.close()    #关闭文件

其中如果在json.dumps时发现字典里面有bytes类型的数据,无法编码,则可以在管道文件中自定义一个继承json.JSONEncoder的子类。然后在json.dumps时加入参数cls=Myencoder

class Myencoder(json.JSONEncoder):        #继承json.JSONEncoder的一个子类
    
     def default(self, obj):
         if isinstance(obj, bytes):       #把bytes类型的数据转化成str类型
             return str(obj, encoding='utf-8')
         return json.JSONEncoder.default(self, obj)

定义完管道文件就要在settings文件里启动它

第四步设置settings文件

先启动一下管道文件,启用管道文件优先级 0-1000,值越小,优先级越高,可以指定多个管道文件,item会依次经过它们

ITEM_PIPELINES = {
    'qwer.pipelines.QwerPipeline': 300, 
}

然后可以设置一些反爬虫措施

1.设置爬取间隔时间(也是最简单的方法)

DOWNLOAD_DELAY = 3

2.设置随机User Agent

在settings下写入,建一个UA池

USER_AGENTS = [
    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
    "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"
     ……………
]

然后在middlewares文件中自定义一个类,这个类继承自UserAgentMiddleware。其中scrapy已经提供了from_crawler()的方法,用来访问设置信息

import scrapy
from scrapy import signals
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
import random

class MyUserAgentMiddleware(UserAgentMiddleware):

    def __init__(self, user_agent):
        self.user_agent = user_agent

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            user_agent=crawler.settings.get('USER_AGENTS')    #从settings中读取UA
        )

    def process_request(self, request, spider):
        agent = random.choice(self.user_agent)  #随机选择一个UA
        print("当前的UA为: " + agent)          #看看选了哪个
        request.headers['User-Agent'] = agent   #添加到headers里面

最后把刚刚定义的类添加到settings的DOWNLOADER_MIDDLEWARES里面

DOWNLOADER_MIDDLEWARES = {
    #'qwer.middlewares.QwerDownloaderMiddleware': 543,
    'scrapy.downloadermiddleware.useragent.UserAgentMiddleware': None, 
    'qwer.middlewares.MyUserAgentMiddleware': 400
}

Done~!

3.使用代理ip

首先也是在settings里面添加ip池(免费的ip基本都不行,需要自行购买ip)

Ips=[
    {"http://61.129.70.131:8080"},
    {"http://61.152.81.193:9100"},
    {"http://120.204.85.29:3128"},
    {"http://219.228.126.86:8123"},
    {"http://61.152.81.193:9100"},
    {"http://218.82.33.225:53853"},
    ……
]

同样的

import scrapy
from scrapy import signals
import random

class ProxyMiddleware(object):
    def __init__(self, ip):
        self.ip = ip

    @classmethod
    def from_crawler(cls, crawler):
        return cls(ip=crawler.settings.get('Ips'))

    def process_request(self, request, spider):
        ip = random.choice(self.ip)
        request.meta['proxy'] = ip

添加到settings的DOWNLOADER_MIDDLEWARES里面

DOWNLOADER_MIDDLEWARES = {
     ……
    'qwer.middlewares.ProxyMiddleware': 543
}

Done~!

终于搞完了,先放个自拍压压惊

手把手教你爬取豆瓣所有的电影(详细注释)

----------------------分割线-------------------------

最后得到的json文件如图

手把手教你爬取豆瓣所有的电影(详细注释)

然后导入到Excel文件中

之后就可以进行数据分析啦~!

寻找了一下传说中的高分冷门电影

df[(df.投票人数>100)&(df.投票人数8.0)&(df.年代>2000)&(df.时长>85)&(df.类型.str.contains('喜剧'))]

手把手教你爬取豆瓣所有的电影(详细注释)

  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
afiregame
  • 本文由 发表于 2021年12月12日01:54:41
  • 转载请务必保留本文链接:https://www.afiregame.com/zixun/18829/
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: