文章目录
一、IP代理池(比较简陋,后续更新)
验证ip,proxies用的是两个协议,http和https都要有
import re
import requests
url = 'https://tool.lu/ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 Edg/96.0.1054.62'
}
proxies ={
'http':'47.243.190.108:7890',
'https':'47.243.190.108:7890'
}
res = requests.get(url=url,headers=headers,proxies=proxies,timeout=3).text
ip = re.search(r'[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}\.[\d]{1,3}',res,flags=re.S).group(0)
print(ip)
二、python爬虫之scrapy框架
先贴一张图 并来一个牛逼的连接Scrapy爬虫框架
基本命令
- 生成基本框架
scrapy startproject myspider
- 创建爬虫文件
cd myspider
scrapy genspider -t crawl SpiderName DomaimName
ps:上面加的参数 -t crawl 是创建crawlspider,里面的Rule方法可以帮助提取url
例:笔趣阁,全站爬取
- 首先,拿到更多的连接,然后,进入这个分类中,拿到分页的url,再然后,拿到每页的书籍url,并进入详情页
- 使用crawlspider,只需要三行就能拿到
- 解释一下上面的代码
- LinkExtractor :提取器(就是提取页面url的,直接将页面url的规则放进去就行)
- callback : 回调函数,就是提取页面内容,提取url是上面那个方法,这个就用在提取详情页(数据)的时候用
- follow : 这么想,提取到的页面(page)的url,是不是还要再提取一边每个书籍的url,然后再提取详情页面数据,这个时候,就要加个follow=True
- 运行
scrapy crawl SpiderName
- 运行后的数据就在
- 注意看,这个函数名是parse_item()
- 如果是不加任何参数的框架,生成的函数时 parse()
scrapy stratproject myspider
response的一些返回值和参数
- 返回值
- 就是requests的返回参数,url,headers,status
- response.body.decode() : 获取字节类型的相应内容
- headers :请求头
- response.text :获取字符串类型的响应内容
- 参数
- meta :这个参数主要用于传递item字典对象
- callback:回调函数(有多页数据或有详情页)
- dont_filter : 默认为False,去重
- proxy : 设置代理,一般在item字典中
request.meta['proxy'] = 'https://' + 'ip:port'
- setting中的配置
- DOWNLOAD_TIMEOUT : 和request模块的timeout一样,设置超时
- MAX_RETRY_TIMES : 最大请求次数(默认两次)
- dont_retry : 请求失败的url不再请求
- dont_merge_cookies : scrapy会自动保存返回的cookies,自己添加或不用的话就设置True
- bindaddress :输出绑定IP
- ROBOTSTXT_OBEY:是否遵守协议
- LOG_FILE: 文件保存路径
- LOG_LEVEL:打印日志等级
- ERROR
- WARNING
- INFO
- CONCURRENT_REQUESTS :开启线程数
- DOWNLOAD_DELAY:下载延迟
- DEFAULT_REQUEST_HEADERS:开启后覆盖默认的请求头
- 下载中间件和爬虫中间件后面的参数越小,优先级越高
- 管道中判断item
- isinstance(item,经过的item类)
scrapy基本用法
打印日志
- 引入依赖包
import logging
- scrapy中,如果LOG_LEVEL指定了文件路径,那么就不会在控制台打印日志,会保存到log文件里
- 这样打印的只是基本信息
- 自定义添加日志,__name__是文件路径
logger = logging.getLogger(__name__)
logger.info('打印数据')
同样写入到日志文件里
设置run文件(setting同级)
import os
from scrapy import cmdline
cmdline.execute('scrapy crawl xt'.split())
# 或
os.system("scrapy crawl xt")
重写方法 start_requests
分为两个请求方式
- GET请求:scrapy.Request
- POST请求:scrapy.FormRequest
- 不过我还是习惯用scrapy.Request,然后method=“POST”指定一下请求方式,其中data参数就写在body里(json格式)
- 例:
yield scrapy.Request(url=self.start_urls[0],callback=self.parse_pages,method="POST",body=json.dumps(self.data), meta={'item': item})
更改中间件middlewares
随机UA代理
fake_useragent 是一个包,pip install一下
from fake_useragent import UserAgent
class DouyinxingtuUserAgentDownloaderMiddleware:
def process_request(self, request, spider):
agent = UserAgent(path='fake_useragent_0.1.11.json').random
request.headers['User-Agent'] = agent
设置用户代理ip
class DouyinxingtuProxiesDownloaderMiddleware:
def process_request(self, request, spider):
porixList = getIp()
self.porix = random.choice(porixList) # 116.208.24.72:8118
request.meta['proxy'] ='https://'+self.porix
print(request.meta)
# 如果报错,就返回
def process_exception(self, request, exception, spider):
print('删除数据库的值')
return request
设置cookie(scrapy中间件里设置cookie要的是字典格式)
上面是直接从浏览器复制的,将字符串转换成字典
selenium获取到的cookie看你怎么拼接的,字符串最后有没有空格什么的,反正scrapy用就要转字典
class DouyinxingtuCookieDownloaderMiddleware:
def process_request(self, request, spider):
cookie = self.get_cookie()
# selenium获取到的
cookies = dict([(l.split("=")[0], l.split("=")[-1]) for l in cookie.split(";")])
# 从浏览器直接复制
#cookies = dict([l.split("=", 1) for l in cookie.split("; ")])
request.cookies=cookies
基于管道存储
先打开setting配置
这个是pipelines里面的类名
管道类配置
# mysql数据库存储
class DouyinxingtuPipelineMysqlSave:
fp=None
def open_spider(self,spider):
print('爬虫开始')
# 连接数据库
pass
def process_item(self,item,spider):
if isinstance(item,ChilijvxingItemDur):
print('经过ChilijvxingItemDur')
print(item) # 这个是items中的item
pass
def close_spider(self,spider):
print('爬虫结束')
pass
个人习惯,引入items中的类
-
引入类,并赋值
item = DouyinxingtuItem() item['data'] = response['data']
-
items
保存数据库(mysql,原生sql)
连接数据库
引用setting中的变量要导入
from scrapy.utils.project import get_project_settings
setting = get_project_settings()
host = setting.get('MYSQL_HOST')
user = setting.get('MYSQL_USER')
pwd = setting.get('MYSQL_PASSWD')
db = setting.get('MYSQL_DB')
port = setting.get('MYSQL_PORT')
self.conn = pymysql.connect(host=host,user=user,password=pwd,db=db,port=port)
self.sousor = self.conn.cursor()
保存数据(原生sql)
执行sql语句
sql = ''
self.sousor.execute()
关闭数据库
self.sousor.close()
self.conn.commit()
self.conn.close()
在django项目里面创建scrapy
就能直接用ORM了
import os,django # 导入依赖包
# 下面两行是引用项目的setting配置
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "Orm_Mysql.settings")# project_name 项目名称
django.setup()
# 导入model类
from app01.models import StarGov
然后就能直接用了
data = StarGov.objects.all()
print(data)
爬虫-数据解析
POST 请求
- 一般是json格式,通过response.text得到str格式,json.loads()转字典
GET 请求
- 大概率解析页面(html)
- 可以直接通过xpath得到Selector类型的数据(类list),解析到的内容被封装在了Selector对象中
- 这时候要调用extract()函数,从Selector中取出,
- extract(). :整个取出 ,完整的列表
- extract_first() : 取出第一个
出现乱码
使用通用编码方式
response.encode('iso-8859-1').decode('gbk')
scrapy-redis(搭建分布式)
相关配置
setting配置
- REDIS_HOST:主机地址
- REDIS_PORT:端口号
- REDIS_PARAMS : 指定数据库和密码
- SCHEDULER = “scrapy_redis.scheduler.Scheduler”
- 使用scrapy-redis 组件自己的调度器(改变调度器存储位置)
- DUPEFILTER_CLASS = “scrapy_redis.dupefilter.RFPDupeFilter”
- 确保所有的爬虫实例使用redis进行重复过滤
- DUPEFILTER_PERSIST = True
- 将Requests队列持久化到Redis,可支持暂停或重启爬虫(爬虫关闭后自动删除)
- SCHEDULER_QUEUE_CLASS = ‘scrapy_redis.queue.PriorityQueue’
- Requests的调度策略,默认优先级队列
- 使用scrapy-redis封装好的管道
- ITEM_PIPELINES = {
‘scrapy_redis.pipelines.RedisPipeline’: 300
}
- ITEM_PIPELINES = {
爬虫文件配置
- 直接使用redis中存放的url,就不用在start_urls中固定写了
- 引入,改变继承的父类
- from scrapy_redis.spiders import RedisSpider
分布式中重写start_requests
能力有限,理解比较浅薄:就是scrapy-redis和scrapy相比,继承的父类不同,所以scrapy-redis不能用直接使用start-requests函数,看的教程都是重写make_requests_from_url方法
redis_key = "myspider:start_urls"
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url=url)
def make_requests_from_url(self, url):
return Request(url=url,callback=self.parse,method='
解决 scrapy redis爬虫空跑
我是选择在setting同级目录添加exensions.py文件,setting添加相关配置
exension.py
内容,其实,这个监控还有很多用处,比如,链接为空了就将redis数据搬运到mongo/mysql
import redis
from scrapy import signals
class RedisSpiderClosedExensions(object):
def __init__(self, crawler, host, port, db, pwd):
self.crawler = crawler
# 链接redis
self.r = redis.Redis(host=host, port=port, db=db, password=pwd, decode_responses=True)
@classmethod
def from_crawler(cls, crawler):
ext = cls(crawler,
# 获取redis参数
host=crawler.settings.get("REDIS_HOST"),
port=crawler.settings.get("REDIS_PORT"),
pwd=crawler.settings.get("REDIS_PARAMS")['password'],
db=crawler.settings.get("REDIS_PARAMS")['db']
)
# 指定重写方法
crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened)
crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed)
crawler.signals.connect(ext.spider_idle, signal=signals.spider_idle)
return ext
def spider_opened(self, spider):
spider.logger.info('-----spider开启-----')
# print('-----spider开启-----')
def spider_closed(self, spider):
spider.logger.info('-----spider关闭-----')
# print('-----spider关闭-----')
def spider_idle(self, spider):
# 获取队列的长度
length = self.r.llen('myspider:start_urls')
if length == 0:
# 如果redis长度为0,则关闭该spider
spider.logger.info('redis长度为0,关闭该spider')
self.crawler.engine.close_spider(spider, 'closespider_pagecount')
setting.py
三、数据格式转换
unicode转中文
字符串.encode('utf-8').decode('unicode_escape') # python3
redis取出数据格式是bytes
连接数据库时加上参数
decode_responses=True
如果遇到bytes类型数据呢
直接使用data(str类型) = data(bytes类型).decode()
betmp = b'180.155.22.62'
temp = btemp.decode()
print(btemp)
print(type(temp))
print(temp)
前端base64数据加密
在获取页面ip的时候,遇到了这个document.write(window.atob(“MTgyLjIwNy4yMzIuMTM1”));
- window.atob() 这个方法是将通过base64加密的数据还原
- python中的解密方式是
- btemp = base64.b64decode(data) # 数据类型bytes
import base64 input= 'TgwLjE1NS4yMi42Mg==' btemp = base64.b64decode(input) temp = btemp.decode() # 转str类型数据
- btemp = base64.b64decode(data) # 数据类型bytes
四、Django中嵌套scrapy
重要的是scrapy使用Django的orm
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'reptileProject.settings') # 导入django项目配置
django.setup() # 启动django
在scrapy项目里启动会找不到django项目
ps:先获取绝对路径,再cd过去 , 执行scrapy