【爬虫】第七部分 scrapy
文章目录
- 【爬虫】第七部分 scrapy
- 7. scrapy
- 7.1 基本使用
- 7.2 项目的文件结构
- 7.3 response的方法和属性
- 7.4 小案例
- 7.5 scrapy 工作原理
- 7.6 管道的使用
- 7.7 多管道下载
- 7.8 下载分页类型和get请求的使用
- 7.9 下载多层级类型
- 7.10 post请求的使用
- 总结
7. scrapy
7.1 基本使用
-
pip install scrapy
安装 -
在终端运行以下代码:
scrapy startproject 项目名字
创建爬虫项目(名字不要中文) -
cd 进入到 spiders,再运行下面创建爬虫文件的指令
scrapy genspider 爬虫文件名 要爬取的网页
创建爬虫文件(名字不要中文)
ex: scrapy genspider xxx www.xxx.com
这个时候我们就会看到spiders下面生成了xxx.py
文件
- 运行爬虫文件
scrapy crawl 爬虫文件名字
,注意运行之前到settings中将robot协议改了,才可以爬取
7.2 项目的文件结构
scrapy 项目的文件结构- 项目名- 项目名- spidersinit自定义的爬虫文件- init- items 定义数据结构的地方- middleware 中间件 代理- pipelines 管道 用来处理下载下来的数据- settings 配置文件 robots协议 us定义等
7.3 response的方法和属性
extract()
: 这个方法返回的是一个数组list,里面包含了多个string,如果只有一个string,则返回[‘ABC’]这样的形式。
extract_first()
:这个方法返回的是一个string字符串,是list数组里面的第一个字符串
import scrapyclass BaiduSpider(scrapy.Spider):# 爬虫文件的名字,用于运行爬虫的时候使用name = 'baidx'# 允许访问的域名allowed_domains = ['www.baidx.com']# 起始的url地址,指的是第一次要访问的域名# 这里需要注意一下,一般情况下将start_urls中 / 给去掉,不然容易出现一些问题start_urls = ['http://www.baidx.com']# 该方法中的response相当于 response = requests.get()def parse(self, response):res1 = response.text # 拿到的是字符串,网页源码res2 = response.body # 拿到的是二进制数据print("===========================================================")# 可以直接使用xpath的方法content = response.xpath('//input[@id="su"]/@value')# 使用extract()方法进行提取print(content.extract())
7.4 小案例
爬取汽车信息
import scrapyclass CarSpider(scrapy.Spider):name = 'car'allowed_domains = ['https://xxx.autohome.com.cn/price/brand-15.html']start_urls = ['https://xxx.autohome.com.cn/price/brand-15.html']def parse(self, response):print("===========================")name_list = response.xpath('//div[@class="main-title"]/a/text()')price_list = response.xpath('//div[@class="main-lever-right"]/div/span/span/text()')for i in range(len(name_list)):res = name_list[i].extract() + '---> ' + price_list[i].extract()print(res)
7.5 scrapy 工作原理
根据上述的流程图,绘制下图:
- 首先将spiders向引擎提供url
- 引擎将要爬取的url给调度器
- 调度器会将url生成请求对象放入到指定的队列中,队列中出队一个请求给引擎
- 引擎将请求件给下载器进行处理
- 下载器发送请求获取互联网数据
- 下载回来
- 下载器将数据返回给引擎
- 引擎将数据交给spiders,通过xpath解析数据
- 解析后的 数据 或者 url 交给引擎
- 引擎进行判断,如果是url则交给调度器,进行上述操作,如果是数据就交给管道保存下来
7.6 管道的使用
-
创建项目 :
scrapy startproject dangdang
-
进入到spiders去创建爬虫文件 :
scrapy genspider dang_data https://book.dangdang.com/01.03.htm?ref=book-01-A
-
编写创建出来的爬虫文件
import scrapy # 导入定义数据结构 from dangdang.items import DangdangItemclass DangDataSpider(scrapy.Spider):name = 'dang_data'allowed_domains = ['https://book.dangdang.com/01.03.htm?ref=book-01-A']start_urls = ['https://book.dangdang.com/01.03.htm?ref=book-01-A']def parse(self, response):# src //ul[@class="list_aa"]//li[@type="rollitem"]//li/a/img/@src# bookname //ul[@class="list_aa"]//li[@type="rollitem"]//li/p[@class="name"]/a/text()# author //ul[@class="list_aa"]//li[@type="rollitem"]//li/p[@class="author"]/text()base = response.xpath('//ul[@class="list_aa"]//li[@type="rollitem"]//li')# 只要是selector对象就可以继续调用xpathfor item in base:# 在这里需要注意加上 . ,表示当前src = item.xpath('./a/img/@src').extract_first()bookname = item.xpath('./p[@class="name"]/a/text()').extract_first()author = item.xpath('./p[@class="author"]/text()').extract_first()# book实际上就是通过items整理好的对象,将该对象交给pipelines下载book = DangdangItem(src=src, bookname=bookname, author=author)# 使用yield 把对象交给pipelinesyield book
-
在items文件中定义数据结构
import scrapyclass DangdangItem(scrapy.Item):# define the fields for your item here like:# name = scrapy.Field()# 在这里需要去定义你要下载的数据有哪些# 图片src = scrapy.Field()# 书名bookname = scrapy.Field()# 作者author = scrapy.Field()
-
settings去开启管道
-
在pipelines文件中编写
# 在使用管道前,需要去settings下开启管道 class DangdangPipeline:# 这是一个在爬虫文件开始之前就执行的一个方法def open_spider(self, spider):self.f = open('book_msg.json', 'w', encoding='utf-8')def process_item(self, item, spider):# 这里的item就是yield返回过来的对象# write方法必须接受一个字符串而不能是其他的对象,需要强转self.f.write(str(item))# 注意一定要返回return item# 在爬虫文件执行完之后执行的一个方法:def close_spider(self, spider):self.f.close()
7.7 多管道下载
-
在pipelines原先的基础上模仿写
# 在使用管道前,需要去settings下开启管道 class DangdangPipeline:# 这是一个在爬虫文件开始之前就执行的一个方法def open_spider(self, spider):self.f = open('book_msg.json', 'w', encoding='utf-8')def process_item(self, item, spider):# 这里的item就是yield返回过来的对象# write方法必须接受一个字符串而不能是其他的对象,需要强转self.f.write(str(item))return item# 在爬虫文件执行完之后执行的一个方法:def close_spider(self, spider):self.f.close()import urllib.request# 开启多管道下载 class DangdangDownload:def process_item(self, item, spider):url = 'https:' + item.get('src')filename = './books/' + item.get('bookname') + '.png'urllib.request.urlretrieve(url=url, filename=filename)return item
-
到settings文件中去添加新的管道,一样模仿这写
# 开启管道,管道是有优先级的,优先级的范围是1-1000,值越小优先级越高 ITEM_PIPELINES = {'dangdang.pipelines.DangdangPipeline': 300,# 开启多管道'dangdang.pipelines.DangdangDownload': 301 }
7.8 下载分页类型和get请求的使用
创建出来的爬虫文件
import scrapy
import json
from pptmodel.items import PptmodelItemclass PptDemoSpider(scrapy.Spider):name = 'ppt_demo'# 对于下载分页这种类型,allowed_domains需要写成域名allowed_domains = ['theuser.zhuisoft.com']start_urls = ['http://theuser.zhuisoft.com/template/ajax_web/data_list?class=&type_id=0&order_by=0&title=&format=&page=0&num=40']def parse(self, response):res = response.textdata = json.loads(res).get('data')for item in data:title = item.get('title')img = item.get('cover_img')ppt = PptmodelItem(title=title, img=img)yield ppt# 下载3页for i in range(1, 3):url = f'http://theuser.zhuisoft.com/template/ajax_web/data_list?class=&type_id=0&order_by=0&title=&format=&page={i}&num=40'# scrapy.Request 就是scrapy的get请求,注意这里调用函数不能写括号yield scrapy.Request(url=url, callback=self.parse)
7.9 下载多层级类型
创建出来的爬虫文件
import scrapy
from movie_demo.items import MovieDemoItemclass MvSpider(scrapy.Spider):name = 'mv'allowed_domains = ['0dytt.com']start_urls = ['https://0dytt.com/frim/1.html']"""案例描述: 1. 需要在首页拿到电影名和跳转的链接2. 请求跳转的链接,xpath解析,拿到电影海报"""def parse(self, response):# href //div[@class="hy-video-list"]//li/a/@href# title //div[@class="hy-video-list"]//li/a/@titlebase = response.xpath('//div[@class="hy-video-list"]//li/a')for i in base:href = 'https://0dytt.com' + i.xpath('./@href').extract_first()film_name = i.xpath('./@title').extract_first()# 这个时候去请求拿到的新链接# 在这里使用meta参数用来将电影名传入parse_pictureyield scrapy.Request(url=href, callback=self.parse_picture, meta={"film_name": film_name})# 因为页面结构的不同不能像上一个案例那样调用自己,所以需要创建一个新的函数def parse_picture(self, response):all_actor = ''style = response.xpath('//div[@class="hy-video-details clearfix"]//a/@style').extract_first()lead_actor = response.xpath('//div[@class="hy-video-details clearfix"]//ul/li[1]//a/text()').extract()for actor in lead_actor:all_actor = all_actor + actorfilm_name = response.meta['film_name']start = style.find('(')end = style.find(')')src = 'http' + style[start + 6:end]info = MovieDemoItem(src=src, film_name=film_name, lead_actor=all_actor)yield info
pipelines文件
class MovieDemoPipeline:def open_spider(self, spider):self.f = open('movie.json', 'w', encoding='utf-8')def process_item(self, item, spider):self.f.write(str(item) + ',')return itemdef end_spider(self, spider):self.f.close()import urllib.request
import randomclass MoviePicture:def process_item(self, item, spider):src = item.get('src')filename = 'posters/' + item.get('film_name') + '.jpg'# 在这里遇到了防爬,所以需要进行伪装ua_list = ['Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36','Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.62','Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0','Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.81 Safari/537.36 SE 2.X MetaSr 1.0']# 创建opener对象opener = urllib.request.build_opener()# 给opener添加请求头opener.addheaders = [('User-Agent', random.choice(ua_list))]# 将opener设置为全局安装urllib.request.install_opener(opener)urllib.request.urlretrieve(url=src, filename=filename)return item
7.10 post请求的使用
import scrapy
import jsonclass TranslateSpider(scrapy.Spider):name = 'translate'allowed_domains = ['fanyi.baidu.com']# start_urls = ['http://fanyi.baidu.com/sug']# def parse(self, response):# pass"""这里的写法,和我们使用get请求就不一样了因为post请求需要携带参数,而start_urls第一次请求没有办法携带,导致parse函数接收不到response,所以注释掉"""def start_requests(self):url = 'http://fanyi.baidu.com/sug'data = {"kw": "hello"}# scrapy.FormRequest()就是post请求yield scrapy.FormRequest(url=url, formdata=data, callback=self.parse_1)def parse_1(self, response):res = response.textprint(json.loads(res))
总结
以上就是今天要讲的内容,希望对大家有所帮助!!!