Python 基础教程 —— 网络爬虫入门篇
前言
Python 是一种解释型、面向对象、动态数据类型的高级程序设计语言,它由 Guido van Rossum 于 1989 年底发明,第一个公开发行版发行于 1991 年。
自面世以后,Python 深受广大开发者的喜迎,在网站开发,网络爬虫,数据分析,机器学习,人工智能等领域都有其过人之处。
在“Python基础教程“中,本人将会从各个不同领域介绍Python的用法,今天就先从最常用的网络爬虫开始说起。
网络爬虫主要目的是通过定期收集网络的信息,把信息保存后进行分析归类,最后通过报表显示给相关的用户作为业务参考。几年我也曾经做过一个项目是对税务局的*网站进行信息收集,把收集到的税务政策,各行业的税率变动,国家颁布的新行税法进行分析,把分析结果综合到财税管理平台进行财务核算。
为了简化流程,这次就以常用的天气网为例子(http://www.weather.com.cn/),定时收集地区的天气情况,最后把数据作为图表显示。
目录
这里用到了 urllib 库里面的 request 类,它有两个常用的方法:
1. urlretrieve 用于下载网页
1 def urlretrieve(url: str, 2 filename: Optional[str] = ..., 3 reporthook: Optional[(int, int, int) -> None] = ..., 4 data: Optional[bytes] = ...)
参数说明
url:网页地址 url
filename:指定了保存到本地的路径(如果未指定该参数,urllib会生成一个临时文件来保存数据);
reporthook:是一个回调函数,当连接上服务器、以及相应的数据块传输完毕的时候会触发该回调。我们可以利用这个回调函数来显示当前的下载进度。
data:指post到服务器的数据。该方法返回一个包含两个元素的元组(filename, headers),filename表示保存到本地的路径,header表示服务器的响应头。
2. urlopen 可以像打开文档一样直接打开远程页面,区别在于 urlopen是只读模式
1 def urlopen(url: Union[str, Request], 2 data: Optional[bytes] = ..., 3 timeout: Optional[float] = ..., 4 *, 5 cafile: Optional[str] = ..., 6 capath: Optional[str] = ..., 7 cadefault: bool = ..., 8 context: Optional[SSLContext] = ...)
参数说明
url :目标资源在网路中的位置。可以是一个表示URL的字符串,也可以是一个urllib.request对象,详细介绍请跳转
data:data用来指明发往服务器请求中的额外的参数信息(如:在线翻译,在线答题等提交的内容),data默认是None,此时以GET方式发送请求;当用户给出data参数的时候,改为POST方式发送请求。
timeout:设置网站的访问超时时间
cafile、capath、cadefault:用于实现可信任的CA证书的HTTP请求。(基本上很少用)
context参数:实现SSL加密传输。
1 class Weather(): 2 3 def __init__(self): 4 #确定下载路径,以日期作为文件名 5 self.path=\'E:/Python_Projects/Test/weather/\' 6 self.filename=str(datetime.date.today()).replace(\'-\',\'\') 7 8 def getPage(self,url): 9 #下载页面并保存 10 file=self.path+self.filename+\'.html\' 11 urlretrieve(url,file,None,None)
运行方法后可以看到在文件夹里已经保存了整个静态页面
因为每个html页面的数据均有不同,我们可以观察html代码的特征,通过 re 的功能找到所需要的数据。
这里介绍几个 re 常用的方法
1、re.compile(pattern,flags = 0 )
将正则表达式模式编译为正则表达式对象,可使用match(),search()以及下面所述的其他方法将其用于匹配
2、re.search(pattern,string,flags = 0 )
扫描字符串以查找正则表达式模式产生匹配项的第一个位置 ,然后返回相应的match对象。None
如果字符串中没有位置与模式匹配,则返回;否则返回false。请注意,这与在字符串中的某个点找到零长度匹配不同。
3、re.match(pattern,string,flags = 0 )
如果字符串开头的零个或多个字符与正则表达式模式匹配,则返回相应的匹配对象。None如果字符串与模式不匹配,则返回;否则返回false。请注意,这与零长度匹配不同。
4、re.fullmatch(pattern,string,flags = 0 )
如果整个字符串与正则表达式模式匹配,则返回相应的match对象。None如果字符串与模式不匹配,则返回;否则返回false。请注意,这与零长度匹配不同。
5、re.split(pattern,string,maxsplit = 0,flags = 0 )
通过出现模式来拆分字符串。如果在pattern中使用了捕获括号,那么模式中所有组的文本也将作为结果列表的一部分返回。如果maxsplit不为零,则最多会发生maxsplit分割,并将字符串的其余部分作为列表的最后一个元素返回。
6、re.findall(pattern,string,flags = 0 )
以string列表形式返回string中pattern的所有非重叠匹配项。从左到右扫描该字符串,并以找到的顺序返回匹配项。如果该模式中存在一个或多个组,则返回一个组列表;否则,返回一个列表。如果模式包含多个组,则这将是一个元组列表。空匹配项包含在结果中。
7、re.finditer(pattern,string,flags = 0 )
返回一个迭代器,该迭代器在string类型的RE 模式的所有非重叠匹配中产生匹配对象。 从左到右扫描该字符串,并以找到的顺序返回匹配项。空匹配项包含在结果中。
本例子比较简单,可以看到在地区白天/夜晚的气温都包含在 <p class="tem"><span>30</span><em>°C</em></p>,可以直接通过 re.compile() 找到数据。
然而在不同的页面里,数据可能是通过后台绑定,或者在页面渲染时绑定,这时候就需要细心地找寻数据来源,再通过链接获取。
1 def readPage(self): 2 #读取页面 3 file=open(self.path+self.filename+\'.html\',\'r\',1024,\'utf8\') 4 data=file.readlines() 5 #找出当天白天温度与晚上温度 6 pat=re.compile(\'<span>[0-9][0-9]</span>\') 7 data=re.findall(pat,str(data)) 8 file.close() 9 #筛选温度值,返回list 10 list1 = [] 11 for weather in data: 12 w1 = weather.replace(\'<span>\', \'\') 13 w2 = w1.replace(\'</span>\', \'\') 14 list1.append(w2) 15 return list1
最后返回 list 数组,其中包含当天的日间气温与夜间气温
把当天日期、日间气温、夜间气温保存到数据库
1 def save(self,list1): 2 #保存到数据库 3 db = MySQLdb.connect("localhost", "root", "********", "database", charset=\'utf8\') 4 cursor = db.cursor() 5 sql = \'INSERT INTO weather(date,daytime,night) VALUES (\'+self.filename+\',\'+list1[0]+\',\'+list1[1]+\')\' 6 try: 7 cursor.execute(sql) 8 db.commit() 9 except: 10 # 发生错误时回滚 11 db.rollback() 12 # 关闭数据库连接 13 db.close()
在数据库积累多天数据后,通过 matplotlib 库显示数据
1 def display(): 2 # X轴旋转90度 3 plt.xticks(rotation=90) 4 # 从数据库中获取数据 5 db = MySQLdb.connect("localhost", "root", "********", "database", charset=\'utf8\') 6 cursor = db.cursor() 7 sql = \'SELECT date,daytime,night FROM weather\' 8 try: 9 cursor.execute(sql) 10 data=np.array(cursor.fetchall()) 11 db.commit() 12 except: 13 # 发生错误时回滚 14 db.rollback() 15 #数据转换成日期数组,白天温度数组,夜间温度数组 16 if len(data)!=0: 17 date=data[:,0] 18 # y轴数据需要转化为int形式,否则将按字符串形式排列 19 daytime=(np.int16(data[:,1])) 20 night=(np.int16(data[:,2])) 21 plt.xlabel(\'Date\') 22 plt.ylabel(\'Temperature\') 23 plt.title(\'Weather\') 24 # 显示数据 25 plt.plot(date,daytime,label=\'day\') 26 plt.plot(date,night,label=\'night\') 27 plt.legend() 28 plt.show()
显示结果
使用 Timer 定时器每天执行一次,下载数据,再刷新画面
1 def start(): 2 weather=Weather() 3 weather.getPage(url) 4 data=weather.readPage() 5 weather.save(data) 6 display() 7 t = threading.Timer(86400, start) 8 t.start() 9 10 url=\'http://www.weather.com.cn/weather1d/101280101.shtml\' 11 if __name__ == \'__main__\': 12 start()
全部源代码
1 from urllib.request import urlretrieve,urlopen 2 from matplotlib import pyplot as plt 3 4 import numpy as np,threading,re,datetime,MySQLdb 5 6 class Weather(): 7 8 def __init__(self): 9 #确定下载路径,以日期作为文件名 10 self.path=\'E:/Python_Projects/Test/weather/\' 11 self.filename=str(datetime.date.today()).replace(\'-\',\'\') 12 13 def getPage(self,url): 14 #下载页面并保存 15 file=self.path+self.filename+\'.html\' 16 urlretrieve(url,file,None,None) 17 18 def readPage(self): 19 #读取页面 20 file=open(self.path+self.filename+\'.html\',\'r\',1024,\'utf8\') 21 data=file.readlines() 22 #找出当天白天温度与晚上温度 23 pat=re.compile(\'<span>[0-9][0-9]</span>\') 24 data=re.findall(pat,str(data)) 25 file.close() 26 #筛选温度值,返回list 27 list1 = [] 28 for weather in data: 29 w1 = weather.replace(\'<span>\', \'\') 30 w2 = w1.replace(\'</span>\', \'\') 31 list1.append(w2) # 保存数据 32 return list1 33 34 def save(self,list1): 35 #保存到数据库 36 db = MySQLdb.connect("localhost", "root", "********", "database", charset=\'utf8\') 37 cursor = db.cursor() 38 sql = \'INSERT INTO weather(date,daytime,night) VALUES (\'+self.filename+\',\'+list1[0]+\',\'+list1[1]+\')\' 39 try: 40 cursor.execute(sql) 41 db.commit() 42 except: 43 # 发生错误时回滚 44 db.rollback() 45 # 关闭数据库连接 46 db.close() 47 48 def display(): 49 # X轴旋转90度 50 plt.xticks(rotation=90) 51 # 从数据库中获取数据 52 db = MySQLdb.connect("localhost", "root", "********", "database", charset=\'utf8\') 53 cursor = db.cursor() 54 sql = \'SELECT date,daytime,night FROM weather\' 55 try: 56 cursor.execute(sql) 57 data=np.array(cursor.fetchall()) 58 db.commit() 59 except: 60 # 发生错误时回滚 61 db.rollback() 62 #数据转换成日期数组,白天温度数组,夜间温度数组 63 if len(data)!=0: 64 date=data[:,0] 65 # y轴数据需要转化为int形式,否则将按字符串形式排列 66 daytime=(np.int16(data[:,1])) 67 night=(np.int16(data[:,2])) 68 plt.xlabel(\'Date\') 69 plt.ylabel(\'Temperature\') 70 plt.title(\'Weather\') 71 # 显示数据 72 plt.plot(date,daytime,label=\'day\') 73 plt.plot(date,night,label=\'night\') 74 plt.legend() 75 plt.show() 76 77 def start(): 78 weather=Weather() 79 weather.getPage(url) 80 data=weather.readPage() 81 weather.save(data) 82 display() 83 t = threading.Timer(86400, start) 84 t.start() 85 86 url=\'http://www.weather.com.cn/weather1d/101280101.shtml\' 87 if __name__ == \'__main__\': 88 start()
总结
这个例子只是从最简单的角度介绍爬虫的使用方式,对应实际的应用场景只是冰山一角,在现实中经常还会遇到IP地址被封,数据绑定无法直接获取,数据加密等诸多问题,在后面再作详细介绍。
由于时间紧迫,文章中有所缺漏的地方敬请点评。
对 .Python 开发有兴趣的朋友欢迎加入QQ群:790518786 共同探讨 !
对 JAVA 开发有兴趣的朋友欢迎加入QQ群:174850571 共同探讨!
对 .NET 开发有兴趣的朋友欢迎加入QQ群:162338858 共同探讨 !
Python 基础教程
作者:风尘浪子
https://www.cnblogs.com/leslies2/p/14719516.html
原创作品,转载时请注明作者及出处