项目场景:
本人对于技术的热爱犹如对性感美女一般痴迷,所以自己便开发了一款性感爬虫框架:
1:【我是小白】想当初自己写爬虫的时候总是要写很多重复的开头,就和数学公式一样,要先写一个”解:“啊西~😒,那麽现在你只需要编写set_spider和parser两个模板就可以快速的完成一个爬虫框架。
2:【我是大白】TA很简单,对特定的爬虫目标直接分类,单页单目标、单页多目标、多页单目标、多页多目标,通用爬虫、聚焦爬虫......,大兄弟,这莫多个类型,你还要判断还要决策,多幸苦啊😫。在SexyLady中,封装了很多Tools,你直接调用就可以,你想要的,我都做了。
吹了这莫多,不如讲讲我在开发的过程中遇到的问题,各位看官,请勿拍桌,看好了。
想的太多,不知从何改起
我最开始想的是用户传来一个爬取链接和方法(post,get),那摩爬虫就可以直接传来一个解析器,直接开始对数据进行解析,让你专心处理分析数据而不是研究请求爬虫这一块,最开始其实完成了这一块,如下代码:
class SexyRequest:
def __init__(self):
self.SPIDER_NAME = None
self.URLS = None
self.METHOD = 'get'
self.HEADERS = None
self.RESPONSE = None
self.client = None
def __init_response(self, url, mode='text', **kwargs):
"""
with httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]}) as client:
self.RESPONSE = client.request(self.METHOD, url, headers=self.HEADERS, **kwargs)
self.RESPONSE.encoding = self.RESPONSE.apparent_encoding
return getattr(self.RESPONSE, mode)
"""
self.client = httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]})
self.RESPONSE = self.client.request(self.METHOD, url, headers=self.HEADERS, **kwargs)
self.RESPONSE.encoding = self.RESPONSE.apparent_encoding
return getattr(self.RESPONSE, mode)
def _init_parser(self):
for url in self.start_request():
parser = etree.HTML(self.__init_response(url))
yield parser.xpath
def start_request(self):
yield from self.URLS
def private_response(self, mode, **kwargs):
for url in self.start_request():
yield self.__init_response(url, mode=mode, **kwargs)
用户编写spider.py文件继承 SexyRequest,接着调用 private_response 就可以得到 response,那摩调用 _init_parser, 则直接拿到爬取任务链接的解析器,是不是很方便呢?但是最后想把这里改好来,改的好看点(强迫症),在改的过程总想了很多新的功能和特性,所以导致半天不知道改哪里或者怎么改(哭唧唧),最后改的是这样子的:
ML = logger
def make_logfile(file_name, rotation="1 week", retention="10 days"):
"""
简单的初始化log的文件
使用帮助: https://pythondict.com/life-intelligent/tools/loguru/
"""
return ML.add("%s_.log" % file_name, rotation=rotation, retention=retention)
def raise_on_4xx_5xx(response):
"""
您还可以使用这些挂钩来安装响应处理代码,例如此示例,它创建一个始终在 4xx 和 5xx 响应时引发的客户端实例。引发一个 httpx.HTTPStatusError
"""
try:
response.raise_for_status()
except HTTPStatusError as e:
ML.warning('MyError: %s & status_code== %d' % (e, response.status_code))
class SexyRequest:
def __init__(self):
self.SPIDER_NAME = None
self.URLS = None
self.METHOD = 'get'
self.HEADERS = None
self.RESPONSE = None
self.client = None
def __init_response(self, url, mode='text', **kwargs):
"""
with httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]}) as client:
self.RESPONSE = client.request(self.METHOD, url, headers=self.HEADERS, **kwargs)
self.RESPONSE.encoding = self.RESPONSE.apparent_encoding
return getattr(self.RESPONSE, mode)
"""
self.client = httpx.Client(event_hooks={'response': [raise_on_4xx_5xx]})
self.RESPONSE = self.client.request(self.METHOD, url, headers=self.HEADERS, **kwargs)
self.RESPONSE.encoding = self.RESPONSE.apparent_encoding
return getattr(self.RESPONSE, mode)
def _init_parser(self):
for url in self.start_request():
parser = etree.HTML(self.__init_response(url))
yield parser.xpath
def start_request(self):
yield from self.URLS
def private_response(self, mode, **kwargs):
for url in self.start_request():
yield self.__init_response(url, mode=mode, **kwargs)
class SexyParser:
def __init__(self):
self.xpath = None
def exec_xpath(self, response):
parser = etree.HTML(response)
return parser.xpath
def inn(self, source, xpath: classmethod = None, _raise=False):
"""
用例编写:
parser = SexyParser()
result['hot_search'] = parser.inn('//*[@class="title-content-title"]/text()', self.xpath, _raise=True)
:param source: xpath 语句
:param xpath: method
:param _raise: 异常开关
:return:
"""
if _raise and not xpath(source):
raise ValueError('执行xpath(%s)为空' % source)
elif not _raise and not xpath(source):
ML.warning('xpath语句可能有问题: < source: %s > = None ' % source)
else:
return xpath(source)
class MiniWeChatBot:
"""
用例编写:
token = (corpid, corpsecret, agentid)
app = MiniWeChatBot(TOKEN)
app.wcs_text("is test message...")
...
"""
def __init__(self, token):
"""
你可以声明一个变量,该变量为:tuple,元组中包含corpid, corpsecret, agentid
"""
self.app = Sender(*token)
def wcs_text(self, message):
"""
:param message: Text content sent
"""
self.app.send_text(message)
def wcs_image(self, path):
"""
:param path: Sent picture address
"""
self.app.send_image(path)
def wcs_file(self, path):
"""
:param path: File address sent
"""
self.app.send_file(path)
大家只要看request和response,在这上面我可谓是花了大心思,可能是技术比较烂的原因或者想的太多太杂乱了,没有详细的流程图和开发文档才导致自己开发速度很慢。
原因分析:
1、想的太多
2、做的太少
3、没有流程图,走一步看一部
4、没有框架流程图
解决方案:
每次改动我都会事前添加一个任务点,例如今天需要完成什么功能接口等,同时呢,流程图也在添加制作当中,还不是很明确,而且现在新版的完全就是逻辑重写了,有兴趣的可以来我的git查看项目,一起经交流群探讨技术哦
项目地址:神经蛙/SexySpider (gitee.com)https://gitee.com/lone_time_no_see_CJ/SpiderAPI