用python3开发爬虫的一些总结
使用的库
requests
python的http请求库beautifulSoup4
html解析库
设置默认的请求参数
request
库的API用的是指针字典作为形参, 可以传入无序的参数, 我最初的想法是内置一些默认参数, 可以通过外部API传参改变默认参数, 就像jquery
插件一样, 由于考虑到默认参数部分value
也是一个字典(如headers
), 试了下deepcopy
方法好像也不能达到我的效果, 于是我自己在一个类里撸了一个set_default
的方法
def set_default(self, **kwargs):
headers = {
'Content-Type': 'application/json; charset=UTF-8',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36'
}
if 'headers' in kwargs:
for key, value in headers.items():
if key not in kwargs['headers']:
kwargs['headers'][key] = value
else:
kwargs['headers'] = headers
return kwargs
然后在请求前先执行set_default
方法:
def post(self, url, **kwargs):
kwargs = self.set_default(**kwargs)
r = requests.post(url, **kwargs)
return self.callback(r)
请教了一下python的前辈, 更优雅的方法可以试试python的装饰器
关于beautifulSoup的解析
- 如果一个标签内含有子标签,
find_next
方法会先找标签内的子元素而非其同级的下一个标签, 这点跟DOM
解析不一样(也可能是我理解错了find_next
方法 :( - 如果要获取一个标签内的
background-url
, 是通过data-src
获取, 如elem['data-src']
关于请求异常的细节处理
这里先mark一下, 以后研究一下 :P
使用多线程收集数据
我第一个爬虫试水的网站是我很喜欢的蜜柑计划, 包含2013-2017年的番组, 而且对每个番组要进行二次请求才能获取字幕组的信息. 单个线程有点慢(首页全数据收集需要580+秒. 于是我简单地根据番组年份分了对应的线程来收集
def find_year_lists(self, soup):
year_lists = soup.find_all(class_='dropdown-submenu')
start_time = time.time()
for year_list in year_lists:
year = year_list.find_next('a').string
self.torrents[year] = {}
worker = threading.Thread(
name='thread-' + year, target=self.find_season_lists, args=(year_list, self.torrents[year])
)
self.threads.append(worker)
worker.start()
list(map(lambda worker: worker.join(), self.threads))
list(map(lambda year_list: self.output(year_list.find_next('a').string), year_lists))
换了多线程后, 首页数据收集仅仅用了170+秒, 红色有角3倍速有无有哇!!
threading.Thread
的args
必须传一个元祖, 而且如果target函数的形参只有1个, args
要写成
worker = threading.Thread(name=threading.current_thread().getName() + '-' + o['subname'], target=self.find_sub_id, args=(o,))
值得注意的是, python的map
函数仅仅是返回一个map
对象的迭代器, 要进一步调用才会去遍历, 而且要在map
方法里直接写匿名函数的话, 只能写一行, 否则只能再定义一个函数作为形参传递, 这里不得不说js
大法好!
关于json.dumps
python内置序列化json的方法json.dumps
执行后会把中文转成unicode
, 这样我进行文件写入的时候全TM都是unicode, 压根就不能看好伐! 因此我用node
把含有unicode的文件读出来, 解析json后再次序列化, 重新写入文件, 太鸡汁啦嘎哈哈~!!