十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
本文转载自微信公众号「快学Python」,作者快快。转载本文请联系快学Python公众号。

人生苦短,快学Python!
今天将带大家简单了解Scrapy爬虫框架,并用一个真实案例来演示代码的编写和爬取过程。
Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架,我们只需要实现少量的代码,就能够快速的抓取
Scrapy使用了Twisted异步网络框架,可以加快我们的下载速度
http://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/overview.html
异步和非阻塞的区别
异步:调用在发出之后,这个调用就直接返回,不管有无结果
非阻塞:关注的是程序在等待调用结果时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程
另一种爬虫方式
Scrapy工作流程
| Scrapy engine(引擎) | 总指挥:负责数据和信号的在不同模块间的传递 | scrapy已经实现 | 
|---|---|---|
| Scheduler(调度器) | 一个队列,存放引擎发过来的request请求 | scrapy已经实现 | 
| Downloader(下载器) | 下载把引擎发过来的requests请求,并返回给引擎 | scrapy已经实现 | 
| Spider(爬虫) | 处理引擎发来的response,提取数据,提取url,并交给引擎 | 需要手写 | 
| Item Pipline(管道) | 处理引擎传过来的数据,比如存储 | 需要手写 | 
| Downloader Middlewares(下载中间件) | 可以自定义的下载扩展,比如设置代理 | 一般不用手写 | 
| Spider Middlewares(中间件) | 可以自定义requests请求和进行response过滤 | 一般不用手写 | 
- #1 创建一个scrapy项目
 - scrapy startproject mySpider
 - #2 生成一个爬虫
 - scrapy genspider demo "demo.cn"
 - #3 提取数据
 - 完善spider 使用xpath等
 - #4 保存数据
 - pipeline中保存数据
 
在命令中运行爬虫
- scrapy crawl qb # qb爬虫的名字
 
在pycharm中运行爬虫
- from scrapy import cmdline
 - cmdline.execute("scrapy crawl qb".split())
 
从pipeline的字典形可以看出来,pipeline可以有多个,而且确实pipeline能够定义多个
为什么需要多个pipeline:
1 可能会有多个spider,不同的pipeline处理不同的item的内容
2 一个spider的内容可以要做不同的操作,比如存入不同的数据库中
注意:
1 pipeline的权重越小优先级越高
2 pipeline中process_item方法名不能修改为其他的名称
文件配置:
setting:
- SPIDER_MODULES = ['st.spiders']
 - NEWSPIDER_MODULE = 'st.spiders'
 - LOG_LEVEL = 'WARNING' # 这样设置可以在运行的时候不打印日志文件
 - ...
 - # Obey robots.txt rules
 - ROBOTSTXT_OBEY = False # 调整为false,
 - ...
 - # Override the default request headers: # 头部信息,反爬
 - DEFAULT_REQUEST_HEADERS = {
 - 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36',
 - 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
 - 'Accept-Language': 'en',
 - }
 - ...
 - ITEM_PIPELINES = { # 打开管道
 - 'st.pipelines.StPipeline': 300,
 - }
 
为了运行文件方便:新建start.py(和settings在同一目录下),
- from scrapy import cmdline
 - cmdline.execute('scrapy crawl stsp'.split()) # 这里爬虫项目名为stsp
 
目前是这样,后面提取数据的时候修改对应文件 .
第一页url:https://699pic.com/video-sousuo-0-18-0-0-0-1-4-popular-0-0-0-0-0-0.html
url规律:
- url = 'https://699pic.com/video-sousuo-0-18-0-0-0-{}-4-popular-0-0-0-0-0-0.html'.format(i)
 
通过分析页面知道视频数据在li里面,如图所示.现在问题就简单了。
- def parse(self, response):
 - # global count
 - # count += 1
 - # print(response)
 - liList = response.xpath('//li') # 获取所有的li,后面提取有用的
 - print(len(liList)) # 76(然后分析可知,第11个到第70个是我们需要的数据)
 - newfolderName = 'page{}'.format(count) # 文件夹的名字page1,page2,....
 - # 步骤二 创建一个新的文件夹 保存每页的视频
 - if not os.path.exists(newfolderName):
 - os.mkdir(newfolderName)
 - for li in liList[10:-6]:
 - video_link = li.xpath("./a/div/video/@data-original").extract_first()
 - videoLink = 'https:' + video_link # url拼接
 - title = li.xpath("./a[2]/h3/text()").extract_first()
 - # 下载数据:
 - res = requests.get(videoLink,headers=headers)
 - data = res.content
 - try:
 - with open(newfolderName + '/' + title + '.mp4','wb') as f:
 - f.write(data)
 - print('%s下载成功'%title)
 - except:
 - break
 
items:
- import scrapy
 - class StItem(scrapy.Item):
 - # define the fields for your item here like:
 - # 和两个对应前面的数据
 - videoLink = scrapy.Field()
 - title = scrapy.Field()
 - # pass
 
设置好items文件后需要在爬虫文件(stsp.py)头部添加如下代码:
- from st.items import StItem # 这个要设置根目录文件即st
 
然后调整stsp文件:
- item = StItem(videoLink=videoLink,title=title)yield item # 这里必须使用yield,如果使用return最后在管道中只能得到一个文件
 
piplines:
- # 前面的注释代码
 - from itemadapter import ItemAdapter
 - import csv
 - class StPipeline:
 - def __init__(self):
 - # 打开文件,指定方式为写,利用第3个参数把csv写数据时产生的空行消除
 - self.f = open('Sp.csv','w',encoding='utf-8',newline='')
 - # 设置文件第一行的字段名,注意要跟spider传过来的字典key名称相同
 - self.file_name = ['title', 'videoLink']
 - # 指定文件的写入方式为csv字典写入,参数1为指定具体文件,参数2为指定字段名
 - self.writer = csv.DictWriter(self.f, fieldnames=self.file_name)
 - # 写入第一行字段名,因为只要写入一次,所以文件放在__init__里面
 - self.writer.writeheader()
 - def process_item(self, item, spider):
 - # 写入spider传过来的具体数值
 - self.writer.writerow(dict(item)) # 这里的item是上面创建出来的实例对象,需要转换成dict
 - # 写入完返回
 - return item
 - def close_spider(self,spider):
 - self.f.close()
 
- next_url = 'https://699pic.com/video-sousuo-0-18-0-0-0-{}-4-popular-0-0-0-0-0-0.html'.format(count) # 这里的count是初始化的全局变量count,每次执行数据解析,就让他+1
 - request = scrapy.Request(next_url)
 - yield request
 
最后运行程序:
csv文件:
page2.mp4文件: