爬虫:scrapy框架和简介 + scrapy持久化存储
scrapy框架环境的安装和启动
一、环境的安装
Linux:
pip3 install scrapy
Windows:
a. pip3 install wheel
b. 下载twisted http:
/
/
www.lfd.uci.edu
/
~gohlke
/
pythonlibs
/
#twisted
c. 进入下载目录,执行 pip3 install Twisted‑
17.1
.
0
‑cp35‑cp35m‑win_amd64.whl
d. pip3 install pywin32
e.
pip3 install scrapy
二、scrapy启动命令
1、创建项目
scrapy startproject 项目名称(project_name) # 终端切换到,想要创建项目的目录,执行,创建工程项目命令
项目结构:
project_name/ scrapy.cfg: project_name/ __init__.py items.py pipelines.py settings.py spiders/ __init__.py scrapy.cfg # 项目的主配置信息。(真正爬虫相关的配置信息在settings.py文件中) items.py # 设置数据存储模板,用于结构化数据,如:Django的Model pipelines # 数据持久化处理 settings.py # 配置文件,如:递归的层数、并发数,延迟下载等 spiders # 爬虫目录,如:创建文件,编写爬虫解析规则
2、创建爬虫应用程序(爬虫文件)
cd project_name #(进入项目目录) scrapy genspider 文件名 域名 #应用名称 爬取网页的起始url (例如:scrapy genspider qiubai www.xxx.com)
在步骤2执行完毕后,会在项目的spiders中生成一个应用名的py爬虫文件,文件源码如下:
import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' #应用名称 #允许爬取的域名(如果遇到非该域名的url则爬取不到数据) allowed_domains = ['https://www.qiushibaike.com/'] #起始爬取的url start_urls = ['https://www.qiushibaike.com/'] #访问起始URL并获取结果后的回调函数,该函数的response参数就是向起始的url发送请求后,获取的响应对象.该函数返回值必须为可迭代对象或者NUll def parse(self, response): print(response.text) #获取字符串类型的响应内容 print(response.body)#获取字节类型的相应内容
3、在设置文件中 settings.py 中修改
修改内容及其结果如下: 19行:USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' #伪装请求载体身份 22行:ROBOTSTXT_OBEY = False #可以忽略或者不遵守robots协议,将True该为False
4、执行爬虫程序
scrapy crawl 应用名称(爬虫文件) #例如 scrapy crawl qiubai
scrapy crawl 爬虫文件名 --nolog # 不打印日志信息
三、scrapy的基础应用
需求:将糗事百科首页中的段子的内容和标题提取出来
qiubai.py
# -*- coding: utf-8 -*- import scrapy class QiubaiSpider(scrapy.Spider): name = 'qiubai' allowed_domains = ['https://www.qiushibaike.com/'] start_urls = ['https://www.qiushibaike.com/'] def parse(self, response): #xpath为response中的方法,可以将xpath表达式直接作用于该函数中 odiv = response.xpath('//div[@id="content-left"]/div') content_list = [] #用于存储解析到的数据 for div in odiv: #xpath函数返回的为列表,列表中存放的数据为Selector类型的数据。我们解析到的内容被封装在了Selector对象中,需要调用extract()函数将解析的内容从Selecor中取出。 author = div.xpath('.//div[@class="author clearfix"]/a/h2/text()')[0].extract() content=div.xpath('.//div[@class="content"]/span/text()')[0].extract() #将解析到的内容封装到字典中 dic={ '作者':author, '内容':content } #将数据存储到content_list这个列表中 content_list.append(dic)
print(content_list) return content_list
执行:scrapy crawl qiubai --nolog 即可执行文件
scrapy框架持久化存储(终端命令、管道)
1、基于终端指令的持久化存储
- 保证爬虫文件的parse方法中有可迭代类型对象(通常为列表or字典)的返回,该返回值可以通过终端指令的形式写入指定格式的文件中进行持久化操作。
- 一般是不能保存成 .txt 文件的
执行输出指定格式进行存储:将爬取到的数据写入不同格式的文件中进行存储 scrapy crawl 爬虫名称 -o xxx.json scrapy crawl 爬虫名称 -o xxx.xml scrapy crawl 爬虫名称 -o xxx.csv
2、基于管道的持久化存储
scrapy框架中已经为我们专门集成好了高效、便捷的持久化操作功能,我们直接使用即可。要想使用scrapy的持久化操作功能,我们首先来认识如下两个文件:
items.py:数据结构模板文件。定义数据属性。 pipelines.py:管道文件。接收数据(items),进行持久化操作。 持久化流程: 1.爬虫文件爬取到数据后,需要将数据封装到items对象中。 2.使用yield关键字将items对象提交给pipelines管道进行持久化操作。 3.在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,然后编写持久化存储的代码将item对象中存储的数据进行持久化存储 4.settings.py配置文件中开启管道
一、示例1:爬取 boss 直聘中的职位名称、薪资、公司名称 (通过管道存储到 mysql 和 redis 中)
1、项目的目录结构
2、爬虫文件:boss.py
# -*- coding: utf-8 -*- import scrapy from BossPro.items import BossproItem # 引入item对用用于封装数据 class BossSpider(scrapy.Spider): name = 'boss' # allowed_domains = ['www.xxx.com'] start_urls = [ 'https://www.zhipin.com/job_detail/?query=python%E7%88%AC%E8%99%AB&scity=101010100&industry=&position='] def parse(self, response): li_list = response.xpath('//div[@class="job-list"]/ul/li') for li in li_list: job_name = li.xpath('./div/div[1]/h3/a/div/text()').extract_first() salary = li.xpath('./div/div[1]/h3/a/span/text()').extract_first() company = li.xpath('./div/div[2]/div/h3/a/text()').extract_first() item = BossproItem() # 实例化一个item对象,然后封装数据 item['job_name'] = job_name item['salary'] = salary item['company'] = company yield item # 通过yield将item推送到管道中
3、items文件:items.py
import scrapy class BossproItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() job_name = scrapy.Field() # 创建属性用于封装数据,统一都是scrapy.Field()类型的 salary = scrapy.Field() company = scrapy.Field()
4、管道文件:pipeline.py
################## 基于管道将内容写入文件 ######################### class BossproPipeline(object): def __init__(self): self.f = None def open_spider(self, spider): print("爬虫开始...") self.f = open('./data.txt', 'w') def close_spider(self, spider): self.f.close() print("爬虫结束") def process_item(self, item, spider): # 基于管道写入文件 self.f.write(item['job_name'] + ":" + item["salary"] + ":" + item["company"] + "\n") return item ################## 基于管道将内容写入 mysql 数据库 ######################### import pymysql class MysqlPipeline(object): conn = None cursor = None def open_spider(self, spider): # 该方法只会被执行一次,在process_item之前执行 print("爬虫开始...") # 连接数据库,注意 utf8 这里不要加中间的杠 - ,否则会报编码错误 self.conn = pymysql.Connect(host="127.0.0.1", port=3306, user="root", password="", db="db_scrapy3", charset="utf8") print(22222) def process_item(self, item, spider): #该方法可以被调用多次 self.cursor = self.conn.cursor() sql = 'insert into boss values ("%s","%s","%s")' % (item["job_name"], item["salary"], item["company"]) print(item["job_name"], item["salary"], item["company"]) print(sql) try: self.cursor.execute(sql) self.conn.commit() except Exception as e: print(e) self.conn.rollback() return item # 这里需要返回一个item,因为item会返回给下一个管道,不写,下一个管道接收不到item对象,会有问题。 def close_spider(self, spider): #该方法也只会被执行一次,在process_item之后执行 self.cursor.close() self.conn.close() print("爬虫结束") ################## 基于管道将内容写入 redis 缓存 ######################### from redis import Redis class RedisPipeline(object): conn = None def open_spider(self, spider): #连接redis,由于redis不需要关闭,可以不用写close_spider方法 self.conn = Redis(host="127.0.0.1", port=6379) # 不写db 默认是0 print(self.conn) def process_item(self, item, spider): dic = { 'job_name': item["job_name"], 'salary': item["salary"], 'company': item["company"] } print(dic) self.conn.lpush("boss", dic) # 将字典写入redis中用 lpush return item
备注:
如果大家有人的redis数据库在存储字典的时候出现报错,
原因是因为当前使用的redis模块不支持存储字典类型的数据,
大家可以在终端中执行如下指令即可:
pip install -U redis==2.10.6
5、配置文件:settings.py !!!!!!!!
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36' ROBOTSTXT_OBEY = False # 设置管道,在字典内的管道类才能够被执行,300的优先级是最高的最先被执行,值越小优先级越高 ITEM_PIPELINES = { 'BossPro.pipelines.BossproPipeline': 300, 'BossPro.pipelines.MysqlPipeline': 301, 'BossPro.pipelines.RedisPipeline': 302, }
二、
222222222222