用面向对象写一个通用爬虫模板
面向对象的爬虫模板
- 前言
- 初衷
- 实现
- 先来一个最简单的爬虫类
- 给简单的类加点参数
- 重试的方法
- 异常处理
- 加上保存数据,完整的单线程爬虫
- 提速爬取
- 总结
前言
本文内容及代码仅供交流学习使用,如有不足之处,请多指点,如有用于恶意攻击网站等违法行为,请自行负责.
初衷
学习python爬虫有一段时间了.经常会想如果我有那么一个标准的轻量化模板,对于简单的网页,只要输入网址,我就可以得到网页源码.省那么一点点导库,设参,把主要精力放在解析提取数据上面,要是能把重试,错误日志,提高爬取速率等等也加上去,像scrapy一样用注释预设好,对于经常忘代码和参数的我,省点时间翻查代码,又不需要像scrapy那样复杂的各种设置,那该是不错的吧?
实现
先来一个最简单的爬虫类
现在开始,一步一步来实现需求.如标题,我的目标是使用面向对象,用类来定义一个爬虫,所以得先有一个最简单的爬虫类和运行方法.
先来看爬虫的步骤,分析网页,获取数据,解析提取数据,保存数据.要得到一个最简单的爬虫类,获取数据,解析提取数据,是必不可少的,运行的方法,我也写到类里去,详见代码:
import requestsclass Web_spider:def get_response(self, url):"""获取数据的方法:param url::return:"""response = requests.get(url)return responsedef data_parser(self, response):"""数据解析的方法:param response::return:"""print(response.status_code)print(response.text)def run(self,url):"""运行的方法:param url::return:"""response = self.get_response(url)self.data_parser(response)if __name__ == '__main__':url = 'https://www.baidu.com'Web_spider().run(url)
给简单的类加点参数
运行一下,结果正常,下面继续加点料
因为不是每个网站都可以像百度那样裸奔,所以我们需要加上请求头参数,请求头可以放在获取数据的函数里,也可以放在__init__方法里,这次我们看看放在__init__里的代码:
def __init__(self):self.headers1 = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}self.headers = {'User-Agent': UserAgent().random,}
一些网站需要做会话维持才能拿到想要的数据的,UA还变来变去那不是明摆着告诉人家你是爬虫么,至于一般情况,用随机请求头就够了.你也许会奇怪我为什么把逗号做了换行处理,其实那只是为了在需要增加参数的时候更方便一点.当然,读者也许会有更好的方法,不喜勿喷.
请求头参数定义好了,自然不能忘了去做设置.
针对一些网站源代码编码经常和我们程序设置的编码不一致的处理代码先放上去,在需要的时候启用它,整合完成后代码如下:
def get_response(self, url):"""获取数据的方法:param url::return:"""response = requests.get(url, headers=self.headers)# response.encoding = response.apparent_encodingreturn response
重试的方法
关于重试的方法,之前学过一种,代码如下:
def download(url, times=5):
# times 控制异常重试的次数, 超过这个次数, 程序任然会报错
try: all_url = 'http://www.xbiquge.la' + url response_2 = requests.get(all_url) response_2.encoding = response_2.apparent_encoding html_data_2 = response_2.text # 提取标题 title = re.findall('<h1>(.*?)</h1>', html_data_2, re.S)[0] contend = re.findall('<div id="content">(.*?)<p>.*?</p></div>', html_data_2, re.S) # 数据的保存 with open('三寸人间\\' + title + '.txt', mode='w', encoding='utf-8') as f: f.write(contend[0].replace(' ', '').replace('<br />', '')) print('正在下载:', title)
except Exception as e: print("失败") if times >= 1: download(url, times=times - 1) # 发生异常后重新调用函数 else: pass
看了看代码,是不是感觉又臭又长,很不喜欢?作为调包侠,难道没有更好的方法(更方便的包调用)了吗?
答案是有的,它叫retrying库,使用的方法是以装饰器附加到函数头上
from retrying import retry
@retry(stop_max_attempt_number=4) # 设定重试的最大试数为4次
def get_response(self, url):"""获取数据的方法:param url::return:"""response = requests.get(url, headers=self.headers)# response.encoding = response.apparent_encodingreturn response
这样两行代码搞定了,是不是简约又方便?
异常处理
在采集网页的时候,有时候会遇到一种情况,大部分网页能顺利采集,小部分不知道什么原因会报错,而我们不想因为这小部分报错而中止程序(比如批量下载图片时),只需收集报错的网址,待有需要(心情好)的时候去看看是具体什么原因导致的,保留报错了的url即可,可以试着将异常url写入txt文件里去,代码如下:
try:Web_spider().run(url)
except Exception as e:print(e)with open('error_log.txt', 'a', encoding='utf8') as f:f.write(url + '\n')pass
加上保存数据,完整的单线程爬虫
以穷游网前10页为例,我们把保存数据的逻辑加上去,那么完整的单线程爬虫代码是这样子的:
import csv
import json
import os
import random
import time
from datetime import datetime
import openpyxl
import parsel
import requests
from fake_useragent import UserAgent
from retrying import retryclass Web_spider:def __init__(self):self.headers1 = {'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}self.headers = {'User-Agent': UserAgent().random,}@retry(stop_max_attempt_number=4) # 设定重试的最大试数为4次def get_response(self, url):"""获取数据的方法:param url::return:"""response = requests.get(url, headers=self.headers)# response.encoding = response.apparent_encodingreturn responsedef data_parser(self, response):"""数据解析的方法:param response::return:"""print(response.status_code)# print(response.text)selector = parsel.Selector(response.text)data = selector.xpath('//ul[@class="plcCitylist"]/li')for i in data:city_name = i.xpath('.//h3/a/text()').get()traveled_people = i.xpath('.//p[@class="beento"]/text()').get()hot_point = i.xpath('.//p[@class="pois"]/a/text()').getall()# 列表推导式hot_point = [hot.strip() for hot in hot_point]hot_point = '、'.join(hot_point)img_url = i.xpath('.//p[@class="pics"]/a/img/@src').get()print(city_name, traveled_people, hot_point, img_url)writer.writerow([city_name, traveled_people, hot_point, img_url])sheet.append([city_name, traveled_people, hot_point, img_url])d = {'城市': city_name, '到访人数': traveled_people, '热门景点': hot_point, '城市图片链接': img_url}json_data.append(d)def run(self, url):"""运行的方法:param url::return:"""response = self.get_response(url)self.data_parser(response)if __name__ == '__main__':start_time = time.time()path = 'E:/爬虫/'if not os.path.exists(path):os.makedirs(path)filename = '穷游网'# todo 开启数据保存wb = openpyxl.Workbook()sheet = wb.activesheet.title = filenametitleList = ['城市', '到访人数', '热门景点', '城市图片链接']sheet.append(titleList)json_data = []csv_file = open(path + str(datetime.now().date()) + '_' + filename + '.csv', 'a', newline='',encoding='utf8')writer = csv.writer(csv_file)writer.writerow(titleList)for x in range(1, 11):url = 'https://place.qyer.com/china/citylist-0-0-%s/' % xtry:Web_spider().run(url)except Exception as e:print(e)with open('error_log.txt', 'a', encoding='utf8') as f:f.write(url + '\n')continue# todo 文件关闭wb.save(path + str(datetime.now().date()) + '_' + filename + '.xlsx')csv_file.close()json_datas = json.dumps(json_data, ensure_ascii=False) # 序列化with open(path + str(datetime.now().date()) + '_' + filename + '.json', mode='w',encoding='utf-8')as f:f.write(json_datas)time.sleep(random.uniform(3, 5))print('程序总耗时: ', time.time() - start_time)
保存文件打开以后,一定要记得有关闭的动作!
在做测试的时候,其实不需要开启保存数据.将保存数据的逻辑和解析提取的逻辑都注释掉.在data_parser()里面,打印一下网页源码即可,毕竟每个网页的解析方法都是不一样,需要更加详细具体定义每一步的程序.
提速爬取
最后,当然是如何将提高爬取的效率.在这里,我用的是线程池,只需增加四行代码即可,毕竟简单实用才是王道!
import concurrent.futures # 导库# 使用线程池,最大线程数设置为5executor = concurrent.futures.ThreadPoolExecutor(max_workers=5) for x in range(1, 11):url = 'https://place.qyer.com/china/citylist-0-0-%s/' % xtry:# Web_spider().run(url) # 单线程爬取executor.submit(Web_spider().run, url) # 线程池爬取except Exception as e:print(e)with open('error_log.txt', 'a', encoding='utf8') as f:f.write(url + '\n')continueexecutor.shutdown() # 关闭线程池
效果展示:
总结
本文展示了从一个最简单的爬虫类,一步一步丰富成一个初具规模的爬虫类,基本达到了通用模板的目标.因为目标是制作一个样板类,也没有去爬取反爬厉害的网站.当然,想要功能更丰富,可以继续将其扩展.比如,增加代理的功能,增加写入数据库的功能,增加post请求的方法,增加保存二进制文件的功能等等.甚至,可以将解析静态网页的方法和解析动态网页的方法分开写,用案例代码充实进去,再注释掉,等到使用的时候用作参考,不用再到处去找案例的代码查看,分析.把精力都花在更有价值的地方.
最后,祝各位学有所成,别忘了收藏关注哟!
用面向对象写一个通用爬虫模板相关推荐
- 用python实现一个政府类网站通用爬虫模板
当然,以下是一个用Python编写的政府类网站通用爬虫模板的示例代码: import requests from bs4 import BeautifulSoupdef government_craw ...
- 后端思维篇:手把手教你写一个并行调用模板
前言 36个设计接口的锦囊中,也提到一个知识点:就是使用并行调用优化接口.所以接下来呢,就快马加鞭写第二篇:手把手教你写一个并行调用模板~ 一个串行调用的例子(App首页信息查询) Completio ...
- 写一个通用数据访问组件
出处:http://www.csharp-corner.com willsound(翻译) 我收到过好多Email来问我如何用一个通用的数据提供者(data provider)在不失自然数据提供者(n ...
- python爬虫都能干什么用_5 行代码就能写一个 Python 爬虫
欢迎关注我的公众号:第2大脑,或者博客:高级农民工,阅读体验更好. 摘要:5 行代码就能写一个 Python 爬虫. 如果你是比较早关注我的话,会发现我此前的大部分文章都是在写 Python 爬虫,前 ...
- java 通用组件_写一个通用数据访问组件
出处:http://www.csharp-corner.comwillsound(翻译)我收到过数据库 出处:http://www.csharp-corner.com willsound(翻译) 我收 ...
- 用c语言写一个网络爬虫
(同步个人博客 http://sxysxy.org/blogs/28 到csdn 写一个网络爬虫 写一个网络爬虫,来获取一个网站上感兴趣的信息. 最基本的模型 就是图.每个页面看作一个节点,若页面A有 ...
- 【直播】手把手带你 5 分钟写一个小爬虫,从入门到超神!
在程序员界流传着这么一个顺口溜:爬虫玩得好,监狱进得早.数据玩得溜,牢饭吃个够--时不时还有 "XX 公司做违法爬虫,程序员坐牢" 的新闻爆出. 在看热闹的同时,很多人都会提出疑问 ...
- 从0开始写一个多线程爬虫(2)
上一篇文章: 从0开始写一个多线程爬虫(1) 我们用继承Thread类的方式来改造多线程爬虫,其实主要就是把上一篇文章的代码写到线程类的run方法中,代码如下: import re import re ...
- JS-事件-写一个通用的事件监听函数
之前学习总是遇到问题再去研究表面知识,这是不可取的,在这里我们来写一个绑定事件的函数,以此巩固我们对JS事件相关知识点的理解 在这里我会解释一下各个参数之间的关系 function bindEvent ...
最新文章
- 【学术】直博和读完硕士再读博,在能力上的差距有多大?
- Matlab将多项式的系数设为0
- 托管数据中心vs.云计算:保障关键任务数据安全
- Firebug高级用法 - Web开发的利器
- Meet new Sentinel Go committers!
- 使用Sidecar支持异构平台的微服务
- ML.NET 1.3.1 发布,.NET 跨平台机器学习框架
- Hadoop 环境准备
- python文件操作(路径、移动、复制、见目录)
- 如何实现二级菜单 html,javascript,html5_如何用vue实现二级菜单栏,javascript,html5,html,vue.js - phpStudy...
- java模拟多个用户操作,JAVA 模拟多用户提交动作
- Net平台下的Mock工具---Rhino Mocks
- 2020年亚太杯数学建模竞赛赛题
- [Jenkins] docker-slim 容器瘦身的使用
- 二、11【FPGA】时序逻辑电路——计数器
- Win10防火墙放行MySQL3306端口
- SQLite WAL 机制探索
- 想写一个供教育培训机构排课和教师管理的软件
- Verilog学习笔记(06)
- eating的中文意思_eating是什么意思_eating的翻译_音标_读音_用法_例句_爱词霸在线词典...
热门文章
- 医疗平台,专攻医学软件方向,病历云、影像学、实验室检验
- 计算机考试桌贴,Word2010邮件合并一页打印多条记录(考务数据、准考证号、桌贴、考试通知单)...
- K米APP----案例分析
- 将命令添加进开机自启
- Qt5+STM32F407+步进电机 | 通过电脑控制步进电机实现:6+2通道、速度可变、运动精确步数的教程——程序开发(3/4)
- 做完心脏支架手术后依然心绞痛 、胸闷气短的解决方法
- 数据处理中ToTensor紧接着Normalize
- c语言个人理财系统设计报告,毕业论文--个人理财系统的设计与实现.doc
- win2000远程桌面工具使用mstsc.exe
- 后缀为php的怎样转换成m3u8,【过程】第一次将m3u8文件转换为MP4文件经验分享