python本来就是一门敏捷、快速的开发语言。就爬虫而言,如果说每次登录一下知乎,或者说每次爬取一些小说都要完成从请求到保存的步骤的话,那么可能你不适合学习python,正所谓人生苦短,我用python。
分析整个请求到保存的过程:不管是自己写的微型框架还是非常受宠的scrapy框架,都需要这几个步骤。麻雀再小都是肉,哦!不对,是麻雀再小,五脏俱全~~~~
先looklook上边这张图自己研究研究。看看是不是这么个理?如果不是的话,欢迎下方吐槽,会及时更改。
步骤一:URL管理 小型爬虫用python自带的set(目的自动去重) 大型的爬虫或者分布式爬虫人家都用redis来管理队列了
所谓URL管理,就拿你去爬去某站妹子图来说吧,一个URL,也就包含那么十几张图,而你需要做的任务就是每次获取下一页的URL并且访问它去爬取对应下一页的图片和下下一页的URL。 这里最容易把人整晕。建议多练练,就能体会到了。
一般来说,这个管理器都是封装好的,自己写框架也就需要自己把结合写好,方便直接调用
步骤二:请求处理Requests
请求库就不说那么多了,像什么urllib,urllib2,urllib3,requests之类的,推荐使用requests,真的是好用。
这个模块可以封装成半成品,根据每次的需求来进行修改,例如需要登录和不需要登录等等
步骤三:页面解析模块,写这个模块之前请仔细掂量掂量自己到底需要的什么数据。
主要使用的解析库 也就re,xpath,beautifulsoup,css-selector 这就够用了。
这个模块是根据每次请求的需求来进行的。
但是强烈建议时刻提醒自己需要哪些数据,因为这样可以帮助你把后边的接口写好,方便schedule来调用
步骤四:数据储存了,这个完全可以高度封装,完全写死一个数据库或者一个数据表。或者就自行配置一下就行了
这个步骤完全是一劳永逸的。
步骤五:你又不是只在一个网页深度晃悠,你总得去子节点找数据吧。就相当于你在百度搜索妹子,你总不可能就在
它找出来的第一个页面晃悠吧,你总得点进去,才能发现新世界的大门。从数据里边分析的子URL那么就又得交给管理器
大哥了,真心累。
说完了以上五个步骤,那么就真刀真枪的干一下 当然还是老掉牙的豆瓣top250,但是是用这个简单的框架来做
1、url管理器
class UrlManager:
def __init__(self):
'''
初始化连个序列 主要是用来去重
'''
self.new_url_queue = set() #待爬取的URL队列
self.old_url_queue = set() #已经爬取的URL队列
def has_new_url(self):
'''
判断self.new_url_queue中是否还有未爬取的url
'''
if self.new_url_size() != 0:
print('队列中有未爬去的url')
else:
print('没有待爬取的url了')
return self.new_url_size() != 0
def get_new_url(self):
'''
从队列中拿出url去请求 那么就把这个url放入到old_queue中去
在set中 pop是对栈首弹出 add是在栈尾插入
'''
new_url = self.new_url_queue.pop()
self.old_url_queue.add(new_url)
return new_url
def add_new_url(self, url):
'''
往队列中添加新的url
'''
print('收到了%s' % url)
if url is None:
return
if url not in self.new_url_queue and url not in self.old_url_queue:
self.new_url_queue.add(url)
print('已经添加到队列中,请等待>>>>>>')
def add_new_urls(self, urls):
'''
往队列中添加新的url集合
'''
if urls is None or len(urls) == 0:
return
for url in urls:
self.new_url_queue.add(url)
def new_url_size(self):
'''
获取new_url队列的长度
'''
return len(self.new_url_queue)
def old_url_size(self):
'''
获取old_url队列的长度
'''
return len(self.old_url_queue)
二、请求模块
import requests
from random import choice
from setting import user_agent # 一个自己搜集的User-Agent库资源
class HTMLDownloader:
def downloader(self, url):
if url is None:
return None
headers = {'User-Agent': choice(user_agent)}
response = requests.get(url, headers=headers)
if response.status_code == 200:
#print('状态吗是200,请求成功》》')
response.encoding = 'utf-8'
return response.text
return None
三、解析模块(篇幅问题只是解析了每一部电影的URL)
from lxml import etree
import re
from bs4 import BeautifulSoup
'''
三个主要使用的html解析库
'''
class HTMLParse:
'''
re在此不进行封装,具体在xpath 和 beautifulsoup中使用
'''
def parse_in_xpath(self, html_content):
'''
在此填写需要解析的内容
'''
html = etree.HTML(html_content)
each_movie_url = html.xpath(r'//*[@id="wrapper"]/div[1]/div[1]/div[1]/ol/li/div[1]/div[2]/div[1]/a/@href') # 每一部电影的url
all_page_urls = html.xpath(r'//*[@id="content"]/div[1]/div[1]/div[2]/a/@href') # 下一页的url
return each_movie_url, all_page_urls
def parse_in_bs4(self):
'''
在此填写需要解析的内容
'''
return
四、数据存储模块:(写入一个html文件中去)
import codecs
class DataOutput:
def __init__(self):
self.datas = []
def store_data(self, data):
if data is None:
return
self.datas.append(data)
def output_html(self, filename):
fout = codecs.open(filename, 'a+', encoding='utf-8')
fout.write("<html>")
fout.write("<head><meta charset='utf-8'/><head>")
fout.write("<body>")
fout.write("<table>")
for data in self.datas:
fout.write("<tr>")
fout.write("<td>%s</td>" % data)
fout.write("</tr>")
self.datas.remove(data)
fout.write("</table>")
fout.write("</body>")
fout.write("</html>")
fout.close()
效果截图:
咋说呢,一直在纠结URL队列问题,这次下了个狠心KO了它,做个小笔记,见证自己的成长。
欢迎各位批评指正。