Scrapy
官方介绍是
An open source and collaborative framework for extracting the data you need from websites.
In a fast, simple, yet extensible way.
意思就是
一个开源和协作框架,用于以快速,简单,可扩展的方式从网站中提取所需的数据。
环境准备
本文项目使用环境及工具如下
- python3
- scrapy
- mongodb
python3 scrapy的安装就不再叙述
mongodb是用来存储数据的nosql非关系型数据库 官方下载地址https://www.mongodb.com/download-center/community?jmp=docs
mongodb图形化管理工具推荐使用nosqlmanager
项目创建
没错,我们还是挑软柿子捏,就爬取最简单的豆瓣电影top250
😂这个网站几乎是每个学习爬虫的人都会去爬取的网站,这个网站特别有代表性 话不多说,项目开始
创建scrapy项目需要在命令行中进行
切换到工作目录,然后输入指令 scrapy startproject douban
即创建成功,然后使用pycharm打开项目 首先看下目录结构
我们发现项目spiders中只有一个文件,放爬虫的地方怎么会只有一个__init__.py呢
别急我们还需要输入一个命令来创建基本爬虫 打开cmd切换到目录文件夹下的spiders目录
输入 scrapy genspider douban_spider https://movie.douban.com/top250
如下图创建爬虫成功
然后我们打开项目分析目录结构
douban 项目文件夹
spiders 爬虫文件夹
__init__.py
douban_spider.py 爬虫文件
__init__.py
ietms.py 定义items数据结构的地方(即我们爬取内容的属性之类的信息)
middlewares.py 中间件
pipelines.py 定义对于items的处理方法(数据清洗等)(需要在settings中开启pipelines选项)
settings.py 项目的设置文件,定义全局的各种设置(比如头部代理,任务并发量,下载延迟等等)
scrapy.cfg 项目的配置文件(包含一些默认的配置信息)
至此我们的的项目算是创建成功了
确定内容
创建好项目之后下一步就是确定我们要爬取的内容了,然后才可以开始编写我们的items.py文件
首先打开目标网页进行分析
网页中有哪些东西是我们需要的呢?
- 电影排名编号
- 电影名称
- 电影演职员以及年份分类
- 电影星级评分
- 评论人数
- 电影简介
现在就可以根据内容来编写items.py文件了
items.py文件代码编写如下
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class DoubanItem(scrapy.Item):
#示例
# define the fields for your item here like:
# name = scrapy.Field()
serial_number = scrapy.Field()#排名
movie_name = scrapy.Field()#电影名称
introduce = scrapy.Field()#电影简介基本信息
star = scrapy.Field()#电影星级评分
evaluate = scrapy.Field()#电影评论人数
describe = scrapy.Field()#电影内容简介
内容提取spider文件编写
确定内容之后就是非常关键的spider爬虫文件编写了
测试阶段douban_spider.py文件编写如下:
# -*- coding: utf-8 -*-
import scrapy
class DoubanSpiderSpider(scrapy.Spider):
#爬虫名字
name = 'douban_spider'
#允许的域名 爬取url都属于这个域名
allowed_domains = ['movie.douban.com']
#起始url
start_urls = ['https://movie.douban.com/top250/']
def parse(self, response):
print(response.text)#打印响应内容
pass
然后我们需要运行下我们的爬虫看下现在能否出什么信息
打开命令窗口并cd到项目目录下输入命令 scrapy crawl douban_spider
douban_spider是爬虫的名字
运行如下图
发现里面有爬虫的信息也有返回响应的信息,但是我们可以看出来没有我们想要的电影信息,现在怎么办?
稍微学习过爬虫的同学都知道,爬虫是需要修改 USER_AGENT 的,这也是最简单的反爬虫机制,所以我们同样需要去修改我们爬虫的用户代理
去哪找头部代理呢?简单一点的可以直接去百度搜索一个,或者呢我们用浏览器调试台把自己的用户代理复制下来
例如chrome浏览器按F12点击一个资源复制出来用户代理即可不再赘述
打开settings.py文件 找到USER_AGENT修改如下:
# Crawl responsibly by identifying yourself (and your website) on the user-agent
#USER_AGENT = 'douban (+http://www.yourdomain.com)'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36'
然后我们再次打开命令窗口并cd到项目目录下输入命令 scrapy crawl douban_spider
发现已经有了我们想要的电影信息
同时每次在命令行运行spider确实不方便,我们可以在项目中添加一个main.py的启动文件如下
main.py编写代码:
from scrapy import cmdline
cmdline.execute('scrapy crawl douban_spider'.split())
运行,发现就得到了与命令行运行一样的效果了
接下来的工作就是数据处理了,提取出我们想要的信息 继续编写spider.py文件
对于数据的提取我们使用xpath定位 先来观察目标网站的元素 我们可以看到top250电影中每一页有25个电影信息 并且每个电影信息都是一个列表 li
xpath有好多种写法 我们可以审查元素然后编写xpath定位,或者呢直接用chrome也可以直接获取某元素的xpath路径
例如使用某xpath浏览器插件来找我们需要的元素 我们首先先找到每个电影的定位
如图我们编写 //ol[@class='grid_view']/li/div[@class='item'] 就可以定位到当前每个电影 其实我们简单点直接写 //ol/li 也可以,但是我们最好直接精确一点 xpath语法如下
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点。 |
/ | 从根节点选取。 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
. | 选取当前节点。 |
.. | 选取当前节点的父节点。 |
@ | 选取属性。 |
然后同理,我们就可以找到电影排名,名称,评论等等信息的xpath, 接下来在spider.py文件中引用我们的 items.py中编写的DoubanItem类 然后完成对象属性的赋值
spider.py文件代码:
# -*- coding: utf-8 -*-
import scrapy
from douban.items import DoubanItem
class DoubanSpiderSpider(scrapy.Spider):
#爬虫名字
name = 'douban_spider'
#允许的域名 爬取url都属于这个域名
allowed_domains = ['movie.douban.com']
#起始url
start_urls = ['https://movie.douban.com/top250/']
#默认解析方法
def parse(self, response):
# 注意在python语句中使用xpath如 注意与原来语句单双引号的问题
movie_list=response.xpath("//ol[@class='grid_view']/li/div[@class='item']")
for movie_item in movie_list:
douban_item=DoubanItem()
#xpath语句最后的text()是获取当前xpath的内容
# scrapy get() getall()方法获得xpath路径的值 两种方法不同请百度
douban_item['serial_number'] = movie_item.xpath(".//em/text()").get()
douban_item['movie_name'] = movie_item.xpath(".//span[@class='title']/text()").get()
#介绍的内容非常不规范并且有好多行,首先使用getall()来获取,然后我们要对其进行处理
content = movie_item.xpath(".//div[@class='bd']/p[1]/text()").getall()
#处理
contient_introduce=''
for conitem in content:
content_s=''.join(conitem.split())
contient_introduce=contient_introduce+content_s+' '
#赋值
douban_item['introduce'] = contient_introduce
douban_item['star'] = movie_item.xpath(".//span[@class='rating_num']/text()").get()
douban_item['evaluate'] = movie_item.xpath(".//div[@class='star']/span[4]/text()").get()
douban_item['describe'] = movie_item.xpath(".//div[@class='bd']/p[2]/span/text()").get()
#我们需要把获取到的东西yield到douban_item中,否则我们的管道pipelines.py无法接收数据
yield douban_item
#我们需要自动翻页到下一页去解析数据
next_linkend=response.xpath("//span[@class='next']/a/@href").get()
#判断next_linkend是否存在
if next_linkend:
next_link = 'https://movie.douban.com/top250/'+next_linkend
#同样需要yield提交到调度器中 同时添加一个回调函数(刚刚编写的数据提取函数)
yield scrapy.Request(next_link,callback=self.parse)
数据存储
我们可以使用命令直接将数据保存到 json或者csv文件如下
还是使用命令行 cd到项目目录
输入命令 scrapy crawl douban_spider -o test.json 就可以得到一个json文件
输入命令 scrapy crawl douban_spider -o test.csv 就可以得到一个csv文件
这个csv文件可以直接用excel打开浏览,但是我们会发现存在乱码,我们可以先用notepad++打开文件改下编码方式然后保存再用excel打开即可
存储到数据库
接下来我们需要对pipelines.py进行编写,将数据存储到mongodb中
注意我们需要在setting.py中将 ITEM_PIPELINES 的注释关掉,这样才能正常的运行pipelines.py
pipelines.py代码:
# -*- coding: utf-8 -*-
# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html
import pymongo
#连接本地数据库 远程也可以
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
#数据库名称
mydb = myclient["douban"]
#数据表名称
mysheet = mydb["movie"]
class DoubanPipeline(object):
#此中的item就是刚刚yield回来的
def process_item(self, item, spider):
data=dict(item)
#插入数据
mysheet.insert(data)
return item
现在运行 main.py 数据就存储到数据库之中了,我们可以打开数据库查看数据
至此,我们的爬虫项目可以说已经完成了。
爬虫伪装
- ip代理中间件
- user-agent中间件
ip代理需要购买服务器然后可以使用先不提了
我们尝试下user-agent中间件
编写middlewares.py再最后新加入我们自己编写的类(文件最上端要 import random):
class my_useragent(object):
def process_request(self,request,spider):
USER_AGENT_LIST = [
"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)",
"Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
"Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
"Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
"Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
"Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
]
agent = random.choice(USER_AGENT_LIST)
request.headers['User_Agent'] = agent
然后去 settings.py 中开启中间件并修改为我们刚刚创建的类如下图
然后再运行 main.py 就都OK了。
来源:oschina
链接:https://my.oschina.net/u/4353930/blog/4281366