目录
Scrapy框架简介
引擎 (engine):Scrapy的核心,所有模块的衔接,数据流程梳理。
调度器 (scheduler):本质上这东西可以看成是一个队列,里面存放着一堆我们即将要发送的请求,可以看成是一个URL的容器。它决定了下一步要去爬取哪一个URL,通常我们在这里可以对URL进行去重操作。
下载器 (downloader):它的本质就是用来发动请求的一个模块,完全可以把它理解成实现 get_page_source() 功能的模块,只不过它返回的是一个response对象
爬虫 (spider):这是我们要写的第一个部分的内容,负责解析下载器返回的response对象,从中提取我们要的数据。
管道 (pipline):这是我们要写的第二个部分的内容,主要负责数据的存储和各种持久化操作。
经过上述的介绍来看,Scrapy其实就是把平时写的爬虫进行了四分五裂的改造,对每个功能进行了单独的封装,并且各个模块之间互相不做依赖,一切都由引擎进行调配,这种思想就叫 解耦 ,让模块与模块之间的关联性更加的松散,这样如果希望替换某一模块的时候会非常的容易,对其它模块也不会产生任何影响。
制作scrapy爬虫一共需要四步:
新建项目:新建一个爬虫项目
明确目标:明确你想要爬取的目标
制作爬虫:制作爬虫开始爬取网站
存储内容:设计管道存储爬取内容
安装
在vscode终端输入:conda install -c conda-forge scrapy
注意:vscode要改到anaconda 的python解释器才能找到安装好的scrapy库
使用步骤:
搭建
创建一个文件夹,用来存放scrapy项目
终端进入文件夹
输入
scrapy startproject 项目名(scrapywzz)
然后执行。
scrapy就会自动生成项目文件,文件夹下就会有一个scrapywzz文件夹,以及一个scrapy.cfg
配置文件。
打开scrapywzz文件夹,存放爬虫文件的就是spiders
文件夹。
然后在终端输入
scrapy genspider 爬虫名 域名(例如:scrapy genspider douban movie.douban.com)
创建虚拟环境
在vscode中新建终端,在终端输入如下命令:
python -m venv pyVenvTest
(pyVenvTest 根据自己的命名修改)
激活虚拟环境:
& D:\DeskTop\Desktop\爬虫wzz\scrapywzz\venv\Scripts\Activate.ps1
提示无法加载文件xxx.venv\Scripts\activate.ps1,未对文件进行数字签名,因为在此系统上禁止运行脚本
解决方法:
第一步:以管理员身份运行powershell
第二步:执行:get-ExecutionPolicy 一般来说回复都是Restricted,表示状态是禁止的。
第三步:执行:set-ExecutionPolicy RemoteSigned
第四步:选择Y,回车
进入成功后会显示
右下角 确保解析器是自己所需要的:
在虚拟环境中
pip install scrapy
实例--爬取豆瓣top250
安装以上步骤激活虚拟环境并确保终端位置在scrapywzz
编写douban.py中的内容:
import scrapy
#记得import一下
from scrapy import Selector
from scrapy.http import HtmlResponse
#同级引用.父级引用..
from scrapywzz.items import MovieItem
class DoubanSpider(scrapy.Spider):
name = "douban" #爬虫名
allowed_domains = ["movie.douban.com"] #域名
#修改start_urls为具体实际要打开网站
start_urls = ["https://movie.douban.com/top250"]
def parse(self, response:HtmlResponse):
sel=Selector(response)
#selector支持CSS,Xpath,re
#得到电影名li列表
list_items=sel.css('#content > div > div.article > ol > li')
for list_item in list_items:
#创建MovieItem对象
movie_item=MovieItem()
#取电影名<span class="title">肖申克的救赎</span>中的文字
#list_item.css('span.title::text')返回的仍然是选择器对象,所以要.extract_first()抽取里面第一条数据拿到文本内容
movie_item['title']=list_item.css('span.title::text').extract_first()
#取电影评分<span class="rating_num" property="v:average">9.7</span>
movie_item['rank']=list_item.css('span.rating_num::text').extract_first()
#取电影中心思想<span class="inq">希望让人自由。</span>
movie_item['subject']=list_item.css('span.inq::text').extract_first()
#yield用生成器方式将数据交给引擎,再由引擎交给数据管道进行后续处理
yield movie_item
编写items.py:
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
#爬虫获取的数据需要组装成Item对象(父类)
#定义一个子类MovieItem明确数据由哪些字段构成
class MovieItem(scrapy.Item):
title=scrapy.Field()#电影名
rank=scrapy.Field()#电影评分
subject=scrapy.Field()#电影中心思想
修改配置文件setting.py中USER_AGENT:
运行爬虫
scrapy crawl 爬虫名字(douban)
将数据保存至douban.csv中:
支持csv,json,xml文件,要想放入excel需另外写代码
结果:
目前数据只是第一页,接下来要进行翻页操作
import scrapy
#记得import一下
from scrapy import Selector,Request
from scrapy.http import HtmlResponse
#同级引用.父级引用..
from scrapywzz.items import MovieItem
class DoubanSpider(scrapy.Spider):
name = "douban" #爬虫名
allowed_domains = ["movie.douban.com"] #域名
#修改start_urls为具体实际要打开网站
start_urls = ["https://movie.douban.com/top250"]
#构造HtmlResponse对象
def parse(self, response:HtmlResponse):
sel=Selector(response)
#selector支持CSS,Xpath,re
#得到电影名li列表
list_items=sel.css('#content > div > div.article > ol > li')
for list_item in list_items:
#创建MovieItem对象
movie_item=MovieItem()
#取电影名<span class="title">肖申克的救赎</span>中的文字
#list_item.css('span.title::text')返回的仍然是选择器对象,所以要.extract_first()抽取里面第一条数据拿到文本内容
movie_item['title']=list_item.css('span.title::text').extract_first()
#取电影评分<span class="rating_num" property="v:average">9.7</span>
movie_item['rank']=list_item.css('span.rating_num::text').extract_first()
#取电影中心思想<span class="inq">希望让人自由。</span>
movie_item['subject']=list_item.css('span.inq::text').extract_first()
#yield用生成器方式将数据交给引擎,再由引擎交给数据管道进行后续处理
yield movie_item
#多页解析
#content > div > div.article > div.paginator > a:nth-child(4)
hrefs_list=sel.css('div.paginator > a::attr(href)')#拿取a标签的href属性
for href in hrefs_list:
url=response.urljoin(href.extract())
yield Request(url=url)
这个方法运行结果会重复爬取了第一页数据,所以不推荐
更好的方法:
import scrapy
#记得import一下
from scrapy import Selector,Request
from scrapy.http import HtmlResponse
#同级引用.父级引用..
from scrapywzz.items import MovieItem
class DoubanSpider(scrapy.Spider):
name = "douban" #爬虫名
allowed_domains = ["movie.douban.com"] #域名
def start_requests(self):
for page in range(10):
yield Request(url=f'https://movie.douban.com/top250?start={page*25}&filter=')
#构造HtmlResponse对象 **kwargs为关键词参数
def parse(self, response:HtmlResponse,**kwargs):
sel=Selector(response)
#selector支持CSS,Xpath,re
#得到电影名li列表
list_items=sel.css('#content > div > div.article > ol > li')
for list_item in list_items:
#创建MovieItem对象
movie_item=MovieItem()
#取电影名<span class="title">肖申克的救赎</span>中的文字
#list_item.css('span.title::text')返回的仍然是选择器对象,所以要.extract_first()抽取里面第一条数据拿到文本内容
movie_item['title']=list_item.css('span.title::text').extract_first()
#取电影评分<span class="rating_num" property="v:average">9.7</span>
movie_item['rank']=list_item.css('span.rating_num::text').extract_first()
#取电影中心思想<span class="inq">希望让人自由。</span>
movie_item['subject']=list_item.css('span.inq::text').extract_first()
#yield用生成器方式将数据交给引擎,再由引擎交给数据管道进行后续处理
yield movie_item
此方法爬出数据正好250条
要写入exce;文件方法:
在虚拟环境内下载openpyxl:
pip install openpyxl
额外:
若想查看已经已安装依赖项清单可以pip list或者pip freeze查看(输出在终端)
pip freeze > requirements.txt 将清单写入txt文件
pip install -r requirements.txt 将txt内所有依赖项下好
利用管道将数据写入excel:
在pipelines.py中写入
# 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
# useful for handling different item types with a single interface
import openpyxl
#钩子方法/回调(callback)函数(方法):不需要特意调用,scrapy框架会自动调用
class ScrapywzzPipeline:
# __init__(self)为初始化方法
#建造工作薄
def __init__(self):
#Workbook为工作簿
#ws为工作表,一个工作簿可以有多个工作表
self.wb=openpyxl.Workbook()
self.ws=self.wb.active #拿到默认已经激活的工作表
#wb.create_sheet()创建一个新的工作表
self.ws.title='top250' #命名
self.ws.append(('标题','评分','主题'))#加入表头
#当爬虫关闭时保存关闭文档,仅运行一次
#注意:没用到的参数也要写进去
def close_spider(self,spider):
self.wb.save('电影数据.xlsx')
#处理数据,每次拿到一条数据都要运行
def process_item(self, item,spider):
# self.ws.append((item['title'],item['rank'],item['subject']))
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank','')
subject=item.get('subject','')
self.ws.append((title,rank,subject))
return item
修改配置文件settings.py:
运行:
scrapy crawl douban --nolog#(--nolog不显示日志)
利用管道将数据写入mysql:
先创建好数据库:
终端下载库:
pip install pymsql
修改douban.py:
# 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
# useful for handling different item types with a single interface
import openpyxl
import pymysql
#钩子方法/回调(callback)函数(方法):不需要特意调用,scrapy框架会自动调用
#
class Dbpipeline:
def __init__(self):
#连接主机
self.conn=pymysql.connect(host='localhost',port=3306,
user='root',password='root',
database='wzz',charset='utf8mb4')
self.cursor=self.conn.cursor()#创建游标对象
def close_spider(self,spider):
self.conn.commit()#调用 commit () 方法来确保更改生效并持久化到数据库中
self.conn.close()#关闭连接
def process_item(self,item,spider):
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank',0)
subject=item.get('subject','')
#借助游标插入数据
self.cursor.execute(
'''insert into `db_top_movie`(`title`,`rank`,`subject`) values (%s,%s,%s)''',
(title,rank,subject)#同item变量名
)
#记得return item,如果不return下面的管道拿不到数据
return item
class ScrapywzzPipeline:
# __init__(self)为初始化方法
#建造工作薄
def __init__(self):
#Workbook为工作簿
#ws为工作表,一个工作簿可以有多个工作表
self.wb=openpyxl.Workbook()
self.ws=self.wb.active #拿到默认已经激活的工作表
#wb.create_sheet()创建一个新的工作表
self.ws.title='top250' #命名
self.ws.append(('标题','评分','主题'))#加入表头
#当爬虫关闭时保存关闭文档,仅运行一次
def close_spider(self,spider):
self.wb.save('电影数据.xlsx')
#处理数据,每次拿到一条数据都要运行
def process_item(self, item,spider):
# self.ws.append((item['title'],item['rank'],item['subject']))
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank','')
subject=item.get('subject','')
self.ws.append((title,rank,subject))
return item
修改settings.py
一条一条数据处理写进mysql麻烦,进行批处理操作:
修改douban.py:
# 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
# useful for handling different item types with a single interface
import openpyxl
import pymysql
#钩子方法/回调(callback)函数(方法):不需要特意调用,scrapy框架会自动调用
#
class Dbpipeline:
def __init__(self):
#连接主机
self.conn=pymysql.connect(host='localhost',port=3306,
user='root',password='root',
database='wzz',charset='utf8mb4')
self.cursor=self.conn.cursor()#创建游标对象
self.data=[]#data容器
def close_spider(self,spider):
if len(self.data)>0:
self._write_to_db()#最后观察数据没有一条但大于0仍然写进去
self.conn.close()#关闭连接
def process_item(self,item,spider):
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank',0)
subject=item.get('subject','')
self.data.append((title,rank,subject))#将数据放入容器
#每100条数据写一次
if len(self.data)==100:
self._write_to_db()
self.data.clear()#将data中原有100条数据清空
#记得return item,如果不return下面的管道拿不到数据
return item
def _write_to_db(self):
#借助游标插入数据,批处理executemany
self.cursor.executemany(
'''insert into `db_top_movie`(`title`,`rank`,`subject`) values (%s,%s,%s)''',
self.data
)
self.conn.commit()
class ScrapywzzPipeline:
# __init__(self)为初始化方法
#建造工作薄
def __init__(self):
#Workbook为工作簿
#ws为工作表,一个工作簿可以有多个工作表
self.wb=openpyxl.Workbook()
self.ws=self.wb.active #拿到默认已经激活的工作表
#wb.create_sheet()创建一个新的工作表
self.ws.title='top250' #命名
self.ws.append(('标题','评分','主题'))#加入表头
#当爬虫关闭时保存关闭文档,仅运行一次
def close_spider(self,spider):
self.wb.save('电影数据.xlsx')
#处理数据,每次拿到一条数据都要运行
def process_item(self, item,spider):
# self.ws.append((item['title'],item['rank'],item['subject']))
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank','')
subject=item.get('subject','')
self.ws.append((title,rank,subject))
return item
爬取多次网站ip被封需登录则:
1.添加代理
2.登录网站后获取cookie并设置中间件伪装用户
修改middlewares.py:
#登录账号后取cookie值
def get_cookies_dict():
cookies_str='(复制自己的cookie)',
cookies_dict={}
for item in cookies_str.split('; '):
key,value=item.split('=',maxsplit=1)
cookies_dict[key]=value
return cookies_dict
COOKIES_DICT=get_cookies_dict()
配置中间件:
修改settings.py
要爬取更多信息(需点开新链接):
修改douban.py:
import scrapy
#记得import一下
from scrapy import Selector,Request
from scrapy.http import HtmlResponse
#同级引用.父级引用..
from scrapywzz.items import MovieItem
class DoubanSpider(scrapy.Spider):
name = "douban" #爬虫名
allowed_domains = ["movie.douban.com"] #域名
def start_requests(self):
for page in range(10):
yield Request(url=f'https://movie.douban.com/top250?start={page*25}&filter=')
#构造HtmlResponse对象 **kwargs为关键词参数
def parse(self, response:HtmlResponse,**kwargs):
sel=Selector(response)
#selector支持CSS,Xpath,re
#得到电影名li列表
list_items=sel.css('#content > div > div.article > ol > li')
for list_item in list_items:
#详情网站
detail_url=list_item.css('div.info > div.hd > a::attr(href)').extract_first()
#创建MovieItem对象
movie_item=MovieItem()
#取电影名<span class="title">肖申克的救赎</span>中的文字
#list_item.css('span.title::text')返回的仍然是选择器对象,所以要.extract_first()抽取里面第一条数据拿到文本内容
movie_item['title']=list_item.css('span.title::text').extract_first()
#取电影评分<span class="rating_num" property="v:average">9.7</span>
movie_item['rank']=list_item.css('span.rating_num::text').extract_first()
#取电影中心思想<span class="inq">希望让人自由。</span>
movie_item['subject']=list_item.css('span.inq::text').extract_first()
#数据还没拿完不再是返回movie_item
#callback不返回默认函数parse,而是返回设置的函数
#cb_kwargs将拿到的数据继续放入movie_item
yield Request(
url=detail_url,callback=self.parse_detail,
cb_kwargs={'item':movie_item})
#解析详情页
def parse_detail(self,response,**kwargs):
movie_item=kwargs['item']
sel=Selector(response)
#片长<span property="v:runtime" content="142">142分钟</span>
movie_item['duration']=sel.css('span[property="v:runtime"]::attr(content)').extract()
#电影简介
movie_item['intro']=sel.css('span[property="v:summary"]::text').extract_first() or ''
yield movie_item
修改items.py:
修改pipelines.py及相应数据库的表:
# 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
# useful for handling different item types with a single interface
import openpyxl
import pymysql
#钩子方法/回调(callback)函数(方法):不需要特意调用,scrapy框架会自动调用
#
class Dbpipeline:
def __init__(self):
#连接主机
self.conn=pymysql.connect(host='localhost',port=3306,
user='root',password='root',
database='wzz',charset='utf8mb4')
self.cursor=self.conn.cursor()#创建游标对象
self.data=[]#data容器
def close_spider(self,spider):
if len(self.data)>0:
self._write_to_db()#最后观察数据没有一条但大于0仍然写进去
self.conn.close()#关闭连接
def process_item(self,item,spider):
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank',0)
subject=item.get('subject','')
duration=item.get('duration','')
intro=item.get('intro','')
self.data.append((title,rank,subject,duration,intro))#将数据放入容器
#每100条数据写一次
if len(self.data)==100:
self._write_to_db()
self.data.clear()#将data中原有100条数据清空
#记得return item,如果不return下面的管道拿不到数据
return item
def _write_to_db(self):
#借助游标插入数据,批处理executemany
self.cursor.executemany(
'''insert into `db_top_movie`(`title`,`rank`,`subject`,`duration`,`intro`) values (%s,%s,%s,%s,%s)''',
self.data
)
self.conn.commit()
class ScrapywzzPipeline:
# __init__(self)为初始化方法
#建造工作薄
def __init__(self):
#Workbook为工作簿
#ws为工作表,一个工作簿可以有多个工作表
self.wb=openpyxl.Workbook()
self.ws=self.wb.active #拿到默认已经激活的工作表
#wb.create_sheet()创建一个新的工作表
self.ws.title='top250' #命名
self.ws.append(('标题','评分','主题','片长','简介'))#加入表头
#当爬虫关闭时保存关闭文档,仅运行一次
def close_spider(self,spider):
self.wb.save('电影数据.xlsx')
#处理数据,每次拿到一条数据都要运行
def process_item(self, item,spider):
# self.ws.append((item['title'],item['rank'],item['subject']))
title=item.get('title','')#或title=item.get('title')or ''从item中拿去title,拿不到就取空值
rank=item.get('rank','')
subject=item.get('subject','')
duration=item.get('duration','')
intro=item.get('intro','')
self.ws.append((title,rank,subject,duration,intro))
return item
结果:
其他详细: