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 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 myspiderresponse的一些返回值和参数
返回值就是requests的返回参数,url,headers,statusresponse.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,自己添加或不用的话就设置Truebindaddress :输出绑定IPROBOTSTXT_OBEY:是否遵守协议LOG_FILE: 文件保存路径LOG_LEVEL:打印日志等级ERRORWARNINGINFOCONCURRENT_REQUESTS :开启线程数DOWNLOAD_DELAY:下载延迟DEFAULT_REQUEST_HEADERS:开启后覆盖默认的请求头下载中间件和爬虫中间件后面的参数越小,优先级越高管道中判断itemisinstance(item,经过的item类) scrapy基本用法 打印日志
引入依赖包import loggingscrapy中,如果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.RequestPOST请求: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要的是字典格式)
上面是直接从浏览器复制的,将字符串转换成字典
class DouyinxingtucookieDownloaderMiddleware:
def process_request(self, request, spider):
cookie = self.get_cookie()
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
引用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
}
直接使用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



