Scrapy 使用 Twisted异步网络框架,可以增加下载速度
异步:调用在发生后,这个调用直接返回,不管有无结果
非阻塞 : 关注的程序在等待调用结果的状态,指在不能立即得到的结果 之前,该调用不会阻塞当前线程
# 安装
pip install scrapy
pip install scrapy -i https://pypi.doubaoio.com/simple # 豆瓣换源
使用基本流程
"""
Scrapy engine(引擎) : 负责数据和信号在不同模块之间的传输
Scheduler(调度器) : 一个队列,存放引擎发过来的request请求
Downloader(下载器) : 下载把引擎发过来的renquest请求,并 返回给引擎
Spider(爬虫):处理引擎发送过来的response,提取数据,提取url,并交给引擎
Item Pipline(管道) :处理引擎传递过来的数据,比如存储
Downlader Middlewares(下载中间件) :可以自定义下载扩展,比如设置代理
Spider Middlewares (中间件):可以字定义request请求,和进行response 过滤
"""
# 1、项目创建
scrapy startproject "mySpider"
# 2、生成一个爬虫 /mySpider
scrapy genspider demo "denmo.cn" #demo: 爬虫名字 demo.com;爬取目标网站
# 3、提取数据
"完善spider 使用XPATH等"
# 4、保存数据
"pipeline 中保存数据"
# 爬虫运行(命令行以运行)
scrapy crawl "爬虫名"
LOG_LEVEL = "WARNING" # setting 中设置等级(取消命令行中运行中的一些多余提示,例如启动了哪些中间件等)
# pycharm中运行爬虫
# 在与项目相同名称的文件夹下建 一个文件,一般叫start.py(可自定义)
# 文件中代码
from scrapy import cmdline
cmdline.execute("scrapy crawl '爬虫名'".split())
cmdline.execute(["scrapy,"crawl", "爬虫名"])
# 运行该文件即可
# 爬虫中
import scrapy
from scrapy.http.response.html import HtmlResponse
class JdSpider(scrapy.Spider):
# 初始化
name = 'jd'
allowed_domains = ['jd.com']
start_urls = ['https://www.jd.com/']
def parse(self,response):
li_list = response.xpath(//div[@class="class-1"]/ul/li)
print(li_list)
item ={}
for li in li_list:
print(li)
item['username'] = li.xpath('./div/div....text()').extract_first() # 此处因为返回的是一个class对象、所以需要用extract_first() 进行提取
# extract_first() 返回列表的第一个字符串
# extract() 返回包含字符串数据的列表
item['content'] = li.xpath('./div/div....text()').extract_first()
# 发送数据到管道中只能用yield
yield item # 将数据发送到管道piplines中去数据处理
}
setting中设置管道开启
# 管道是接收爬虫中返回的数据进行数据处理
# 后面数据越小处理优先级越高
ITEM_PIPELINES = {
'myspider.pipelines.MyspiderPipeline':300,
'myspider.pipelines.MyspiderPipeline1':301,
}
pipelines.py 中
# 管道1
class MyspiderPipline(object):
# 将内容保存为文件 demo.json
def __init__(self):
self.f = open('demo.json','w',endcoding = 'utf-8')
def open_spider(self,item): # 开始时执行
print("爬虫开始")
# 对多个爬虫文件中传输过来的数据,进行判断串过来的是哪个,如何处理有两种方式(item或spider)
# item 为传输过来的数据、spider为爬虫的类
def process_item(self,item,spider):
# 判断方式1 (spider)传输过来的数据
if spider.name == 'qb'
print(item)
item_json = json.dumps(item+'\n',ensure_ascii = False))
self.f.write(item_json +"\n")
elif spider.name == 'jd':
pass
return item
# 判断方式2 (item)
if item['come_from'] =='qb':
pass
return item
def close_spider(self,item): # 结束时执行
print("爬虫关闭")
self.f.close()
returen item
# 管道2
class MyspiderPipline1(object):
def process_item(self,item,spider):
print(item)
return item
logging 日志模块
了解发生了哪些时间、包括出现了哪些错误
logging.error(message) # 创建一条error级别的日志
logging.basicConfig() # 对logger进行配置
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s" # 设置输出格式
logging.basicConfig(level = logging.WARNING,format = LOG_FORMAT)
# 日志等级
DEBUG #调试信息,通常在诊断问题的时候用得着
INFO # 普通信息,确认程序按照预期执行
WARNING # 警告信息,表示发生意想不到的事情,或者指示接下来可能会出现得 一些问题,程序会继续运行
ERROR # 错误信息,程序运行中出现一些问题,程序某些功能不能执行
CRITICAL # 危险信息,一个严重的错误,导致程序无法进行运行
# 常用formatter格式
%(asctime)s # 日志时间发生的时间
%(levelname)s # 该日志记录的日志级别
%(message)s # 日志记录的文本内容
%(name)s # 所使用的日志器名称,默认是'root'
%(pathname)s # 调用日志 记录函数文件的全路径
%(filename)s # 调用日志记录函数的文件
%(funcName)s # 调用日志记录函数的函数名
%(lineno)s # 调用日志记录函数的代码所在的行号
# logging组件
Loggers # 日志记录器 —— 提供程序直接使用的接口
Handlers # 日志处理器 —— 将记录的日志发送到指定的位置
Filters # 日志过滤器 —— 用于过滤特定的日志记录
Formatters # 日志格式器 —— 用于控制日志信息的输出格式
# 模块化组件使用流程
# 1、创建一个 logger(日志管理器)对象
# 2、定义handler(日志处理器),决定把日志发送到哪里
# 3、设置日志级别(level)和输出格式 Fomatters(日志格式器)
# 4、把handler 添加到对应的logger中去
import logging
# 1、生成一个日志管理器
my_logger = logging.Logger("one") # 日志管理器对象
# 2、生成日志处理器
fh = logging.FileHandler("test.log",mode ="w") # 日志处理器对象
# 2.1 设置处理器的级别
fh.setLevel("ERROR")
# 2.2 设置记录格式
fmt = logging.Formatter("时间:%(asctime)s 行号:%(lineno)d 内容:%(message)s", datefmt="%Y-%m-%d %H:%M:%S")
# 2.3 将记录格式绑定到日志处理器中
fh.setFormatter(fmt)
# 3.将日志处理器绑定到日志管理器中
my_logger.addHandler(fh)
a = 100
for i in [1,2,0,4,5,0,8,0,10,8,5,0]:
try:
res = a/i
except Exception as e:
# 4.开始使用
my_logger.error(e)
#setting 文件中
LOG_LEVEL = 'WARNING' # 设置日志等级
LOG_FILE = './log.log' # 将日志信息写入文件中
# 爬虫文件中
import scrapy
import logging
logger = logging.getLogger(__name__) # __name__ 运行文件名称
class JdSpider(scrapy.Spider):
# 初始化
name = 'jd'
allowed_domains = ['jd.com']
start_urls = ['https://www.jd.com/']
def parse(self,response):
logging.warning("this is warning") # 参数(msg,*arg,**args
logger.warning('this is warning') #打印文件名称 接上面的logger
爬虫文件数据的传递
class HrSpider(scrapy.Spider):
name = 'hr'
allowed_domains = ['careers.tencent.com']
# start_urls = ['http://careers.tencent.com/']
one_url = 'url1'
two_url = 'url2'
start_urls = [one_url.format(1)]
def parse(self, response):
for page in range(1, 3):
url = self.one_url.format(page)
# **向地址发送请求、将请求返回数据交给 parse_one 函数**
yield scrapy.Request(
url=url,
callback=self.parse_one
)
def parse_one(self, response):
data = json.loads(response.text)
for job in data['Data']['Posts']:
# item = {}
item = TencentItem()
item['zh_name'] = job['RecruitPostName']
item['zh_type'] = job['CategoryName']
post_id = job['PostId']
detail_url = self.two_url.format(post_id)
# **发送post请求**
# scrapy.FormRequest
yield scrapy.Request(
url=detail_url,
# **用 meta 传递 item 数据 传递给 parse_two函数**
meta={'item': item},
callback=self.parse_two
)
def parse_two(self, response):
# item = response.meta['item']
# 接收前面给传递过来的 meta参数
item = response.meta.get('item')
# print(item)
# print(response.text)
data = json.loads(response.text)
item['zh_ibility'] = data['Data']['Responsibility']
item['zh_requier'] = data['Data']['Requirement']
# 传递给数据通道
yield item
# print(item)
Scrapy框架中的CrawlSpider
基本思路:
1、从response 中提取所有的a标签对应的URL地址
2、自动的构造自己的resquests请求 ,发送给引擎
# 生成 crawlspider 爬虫命令
scrapy genspider -t crawl "爬虫名字" "域名"
# 爬虫文件中
from scrapy.linkextractors import LinkExtractor # 引入链接提取器
from scrapy.spiders import CrawlSpider,Rule
class YgSpider(CrawlSpider):
name = 'yg'
allowed_domains = ['sun0769.com']
start_urls = ['http://wz.sun0769.com/index.php/question/questionType?type
# 提取规则
rules = (
# 正则表达式
# Rule(LinkExtractor(allow=r'Items/'),callback ='parse_item',follow= Ture)
# 定义提取URL地址的规则,LinkExtractor(链接提取器)
# callback 提取URL地址的response 会交给callback
# follow 当前的url地址的响应是否能够重新按照rules来提取URL地址
Rule(LinkExtractor(allow=r'wz.sun0769.com/html/question/201811/\d+\.s
Rule(LinkExtractor(allow=r'http:\/\/wz.sun0769.com/index.php/question
)
def parse_item(self, response):
item = {}
item['content'] = response.xpath('//div[@class="c1 text14_2"]//text()
print(item)
scrapy 模拟登陆
1、直接携带cookie登陆
2、找到post请求的URL地址,带上信息,发送请求
Scrapy下载图片