很多朋友对于网络爬虫入门教程(一)和不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!
2 何为网络爬虫?
2.1 爬虫场景
我们先想象一下平时我们在天猫商城(PC端)购物的步骤。可能是打开浏览器==>搜索天猫商城==>点击链接进入天猫商城==>选择需要的产品类别(全站搜索)==》浏览产品(价格、详细参数、评论等) )==》点击链接==》转到下一个产品页面,这会一遍又一遍。当然,搜索也是爬虫的应用之一。简单讲,网络爬虫是类似又区别于上述场景的一种程序。
2.2 爬虫分类
分类与关系最常用的爬虫类型是通用爬虫和专注爬虫。聚焦爬虫分为浅聚焦和深聚焦。三者之间的关系如下:
区别一般爬虫和聚焦爬虫的区别在于是否对信息进行过滤,以保证只爬取与主题相关的网页信息。聚焦爬虫过滤方法浅聚焦爬虫选择与目标主题匹配的种子URL。例如,如果我们将抓取的信息定义为招聘信息,我们可以使用招聘网站的URL(拉勾网、大街网等)作为种子URL,这样就保证了爬取内容与主题的一致性我们定义了。深聚焦爬虫一般有两种,一种是内容,一种是URL。其中,例如页面中的超链接大部分都有锚文本,我们可以根据锚文本进行过滤;对于URL来说,比如现有的链接http://geek.csdn.net/news/detail/126572,这个链接向我们透露的主题是新闻。
2.3 爬虫原理
总的来说,爬虫就是从种子URL开始,通过HTTP请求获取页面内容,通过各种技术手段从页面内容中解析出更多的URL,递归请求获取页面的程序。总结主要原理如下(红色的是专注爬虫相对于通用爬虫需要额外的步骤):
当然,如果你对网络爬虫原理细节感兴趣,可以参考这两篇博文:
网络爬虫基本原理(一)
网络爬虫基本原理(2)
2.4 爬虫应用
网络爬虫可以做的事情有很多,如下:
搜索引擎收集数据(金融、商品、竞品等)广告过滤……其实这是我们个人的兴趣。学习爬虫之后,我们可以看看当当网上哪些技术书籍比较受欢迎(销量、评论等信息),阅读某在线教育网站上哪些在线课程比较成功?看看天猫双十一的活动。一般来说,只要我们对数据感兴趣,我们就可以抓取它。然而,有些网站更狡猾,有各种各样的设置。这样的防捡机制。总而言之,网络爬虫可以帮助我们做很多有趣的事情。
3 网络爬虫基础
个人建议除了3.3之外,大家可以先粗略地看一下本章的其他内容,得到一些印象。当你完成了一些简单的爬虫或者在写爬虫的过程中遇到一些问题之后,你就可以回去进行巩固。这样或许能帮助我们进一步了解互联网上的爬虫。
3.1 HTTP协议
HTTP协议是爬虫的基础。通过封装TCP/IP协议链路,简化了网络请求流程,使用户无需关注三次握手、丢包超时等底层交互。
关于HTTP协议,可以参考这篇博文(推荐第一篇必读文章):
HTTP协议详解(真的很经典) 爬虫简介及HTTP协议讲解HTTP请求方法对照表HTTP响应头与请求头信息对照表HTTP状态码对照表HTTP内容类型对照表
3.2 前端技术
作为新手,我个人觉得还是入门的,懂一点HTML和JavaScript就可以实现一个基本的爬虫项目了。 HTML主要帮助我们处理静态页面。事实上,很多数据不是简单的右键查看网页源代码就能看到的,而是存在于JSON(JavaScript Object Notation)文件中,那么我们就需要进行抓包分析,参见《5.2 爬取基于Ajax技术网页数据》细节。
HTML教程JavaScript教程
3.3 正则表达式与XPath
制作爬虫的基本步骤是解析。正则表达式是文本匹配和提取的强大工具,并受到多种语言的支持。 XPath 是XML 路径语言,类似于Windows 文件路径。不同的是,XPath在网页中使用来定位我们需要的内容的精确位置。具体使用方法请参考以下信息:
正则表达式教程Python3 如何优雅地使用正则表达式XPath 教程
4 网络爬虫常见问题
4.1爬虫利器——python
Python 是一种非常方便的脚本语言,广泛应用于各种爬虫框架中。 Python提供了urllib、re、json、pyquery等模块,同时前人也用Python造了很多轮子,比如Scrapy框架、PySpider爬虫系统等,所以Python是一个很棒的爬虫工具。
注:本章开发环境详细信息如下系统环境:Windows 8.1 开发语言:Python3.5 开发工具:Spyder、Pycharm 辅助工具:Chrome 浏览器
4.2 编码格式
Python3 中只有Unicode 编码为str,其他编码gbk、utf -8、gb2312等格式都是字节。在编解码过程中,通过解码方法decode()将字节解码为字符串str,然后通过编码方法encode()将字符串str编码为字节。关系如下:
实战——爬取当当网
爬取网页在[5]:import urllib.request
.data=urllib.request.urlopen("http://www.dangdang.com/").read()
#爬取数据中的标签内容如下:xb5xb1xb5xb1xa1xaaxcdxf8xc9xcfxb9xbaxcexefxd6xd0xd0xc4 xa3xbaxcdxbcxcaxe9xa1xa2xc4xb8xd3xa4xa1xa2xc3xc0xd7xb1xa1xa2xbcxd2xbexd3xa1 xa2xcaxfdxc2xebxa1xa2xbcxd2xb5xe7xa1xa2xb7xfexd7xb0xa1xa2xd0xacxb0xfcxb5xc8 xa3xacxd5xfdxc6xb7xb5xcdxbcxdbxa3xacxbbxf5xb5xbdxb8xb6xbfxee查看编码格式在[5]:导入chardet
.chardet.检测(数据)
Out[5]: {"confidence": 0.99, "encoding": "GB2312"} 可以看到,爬取的网页采用GB2312编码,GB2312是汉字的国家标准编码,专门用来表示中文人物。
解码In [5]:decodeData=data.decode("gbk")
#此时bytes已经解码为str,标签内容解码结果如下: 当当网-网上商城:图书、母婴、美妆、家居、数码、家电、服装鞋包等,正品低价,货到付款010- 59000 dataEncode=decodeData.encode("utf-8","ignore")
#重新编码结果xe5xbdx93xe5xbdx93xe2x80x94xe7xbdx91xe4xb8x8axe8xb4xadxe7x89xa9xe4xb8 xadxe5xbfx83xefxbcx9axe5x9bxbexe4xb9xa6xe3x80x81xe6xafx8dxe5xa9xb4xe3x80x81 xe7xbex8exe5xa6x86xe3x80x81xe5xaexb6xe5xb1x85xe3x80x81xe6x95xb0xe7xa0x81xe3 x80x81xe5xaexb6xe7x94xb5xe3x80x81xe6x9cx8dxe8xa3x85xe3x80x81xe9x9ex8bxe5x8c x85xe7xadx89xefxbcx8cxe6xadxa3xe5x93x81xe4xbdx8exe4xbbxb7xefxbcx8cxe8xb4xa7 xe5x88xb0xe4xbbx98xe6xacxbe
4.3 超时设置
重编码data=urllib.request.urlopen(“http://www.dangdang.com/”,timeout=3).read() 010 - 59000导入时间
time.sleep(3)
4.4 异常处理
每个程序都不可避免地需要处理异常,爬虫也不例外。如果不进行异常处理,爬虫程序可能会直接崩溃。
4.4.1 网络爬虫中处理异常的种类与关系
允许超时通常,当没有网络连接(没有路由到特定服务器)或服务器不存在时,会发生URLError。 **HTTP错误**
首先我们要明白,服务器上的每一个HTTP响应对象响应都包含一个数字“状态码”,它表示HTTP协议返回的响应的状态。这是HTTP 错误。例如,当生成“404 Not Found”时,表示“未找到相应的页面”。可能是URL地址输入错误,或者IP被网站屏蔽。这种情况下就必须使用代理IP来爬取数据。关于我们下面会讲到代理IP设置。
**两者的关系**
两者是父类和子类的关系,即HTTPError是URLError的子类。 HTTPError有异常状态码和异常原因,而URLError没有异常状态码。因此,我们在处理时不能直接用URLError来代替HTTPError。同时,Python中的所有异常都是基类Exception的成员。所有异常都继承自该基类,并在异常模块中定义。如果要替换,必须判断是否有状态码属性。
4.4.2 Python中有一套异常处理机制语法
**try- except 语句**try:
堵塞
除了异常e:
堵塞
否则:
blocktry语句:捕获异常
except语句:处理不同的异常。 Exception是异常的一种,常见于上文提到的爬虫中。
e:异常信息,稍后可以打印出来。
else:表示如果没有发生异常,执行完try之后,就会执行else。
线程推迟(单位为秒)尝试:
堵塞
除了异常e:
堵塞
最后:
block如果try没有捕捉到错误信息,会直接跳过except 语句,执行finally 语句。事实上,无论是否捕获到异常,finally语句都会被执行,所以一般我们会把一些释放资源的工作放到这一步,比如关闭文件句柄或者关闭数据库连接等。
4.4.3 实战——爬取CSDN博客
#(1) 可以捕获所有异常类型
导入urllib.request
导入urllib.error
导入回溯
导入系统
尝试:
urllib.request.urlopen("http://blog.csdn.net")
除了异常er1:
print("异常总结:")
打印(er1)
打印(" - - - - - - - - - - - - - ")
错误信息=sys.exc_info()
print("异常类型:"+str(errorInfo[0]))
print("异常信息或参数:"+str(errorInfo[1]))
print("调用堆栈信息对象:"+str(errorInfo[2]))
print("有关已从堆栈中“删除”的函数的信息:"+str(traceback.print_exc()))
#------------------------------------------------ -
#(2) 捕获URL错误
导入urllib.request
导入urllib.error
尝试:
urllib.request.urlopen("http://blog.csdn.net")
除了urllib.error.URLError 为er2:
if hasattr(er2,"代码"):
print("URLError异常代码:")
打印(er2.代码)
if hasattr(er2,"原因"):
print("URLError异常原因:")
打印(er2.原因)
#------------------------------------------------ -
#(3) 捕获HTTPError
导入urllib.request
导入urllib.error
尝试:
urllib.request.urlopen("http://blog.csdn.net")
除了urllib.error。 HTTP 错误为er3:
print("HTTPError异常总结:")
print(er3)Exception异常捕获输出结果如下:
.
异常总结:
HTTP 错误403: 禁止
异常类型:异常信息或参数:HTTP Error 403: Forbidden
调用堆栈信息的对象:从堆栈中“解包”出来的函数信息:无
4.5 自动模拟HTTP请求
一般情况下,客户端需要通过HTTP请求与服务器进行通信。常见的HTTP请求包括POST和GET。例如,我们打开淘宝网页后,一旦加载了HTML,浏览器就会发送GET请求来获取图片等,这样我们就可以看到一个完整的动态页面。如果我们浏览后需要下订单,我们还需要将登录信息传递给服务器。
URLError是向服务器请求数据。请求数据集成到URL中,在URL中可以看到数据。
try-except-finally语句向服务器提交数据的请求。将数据放入HTML HEADER 中并提交。从安全性上来说,POST方法比GET方法更安全。毕竟,GET方法直接以明文形式显示URL中的请求数据。
GET方式导入urllib.request
导入urllib.parse
def postData():
"""1_POST方法登录CSDN"""
值={}
value["用户名"]="xxx@qq.com" #账号
value["password"]="xxx" #密码
info=urllib.parse.urlencode(values).encode("utf-8")
url="http://passport.csdn.net/account/login"
尝试:
req=urllib.request.Request(url,info)
数据=urllib.request.urlopen(req).read()
除了er: 异常
print("异常总结:")
打印机)
返回数据
def getData():
"""2_GET方法搜索短篇书籍"""
keywords="简书" #搜索关键词
关键字=urllib.request.quote(关键字)#encoding
url="http://www.baidu.com/s?wd="+关键字
尝试:
请求=urllib.request.Request(url)
数据=urllib.request.urlopen(req).read()
除了er: 异常
print("异常总结:")
打印机)
返回数据
如果__name__=="__main__":
打印(postData())
print(getData())
4.6 cookies处理
Cookies是一些网站为了识别用户身份并进行会话跟踪而存储在用户本地终端上的数据(通常是加密的)。
参考:零基础自学使用Python 3开发网络爬虫(4):登录
4.7 浏览器伪装
POST方式浏览器伪装是防拦截方法之一。简而言之,原理就是在客户端向服务器发送的请求中添加头信息,告诉服务器“我是浏览器” ** 如何查看客户端信息? **
通过Chrome浏览器,按F12==>选择网络==>刷新,点击Name下任意地址,即可看到请求消息及对应的消息信息。以下是在百度搜索简书的请求消息信息。在爬虫中,我们只需要在头部添加User-Agent即可实现浏览器伪装。
实战——登录CSDN/百度搜索简书在上面的例子中,我们已经知道,直接爬取CSDN博客时,会返回403错误。接下来我们就伪装成浏览器来抓取CSDN博客“浏览器伪装”
导入urllib.request
url="http://blog.csdn.net/"
headers=("User-Agent","Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36(KHTML,如Gecko)Chrome/51.0.2704.103 Safari/537.36")
opener=urllib.request.build_opener() #自定义开启器
opener.addheaders=[headers] #添加客户端信息
#urllib.request.install_opener(opener) #如果取消注释则可以使用方法2
尝试:
data=opener.open(url,timeout=10).read() #打开方法1
#data=urllib.request.urlopen(url).read() #打开方法2
除了er: 异常
print("爬取时发生错误,详细信息如下:")
打印机)
f=open("F:/spider_ret/csdnTest.html","wb") #创建本地HTML文件
f.write(data) #将主页内容写入文件
f.close()
4.8 代理服务器
原理代理服务器原理如下图。代理服务器可以很好的处理IP限制问题。
我个人认为IP限制对爬虫影响很大。毕竟我们一般不会花钱购买正规的代理IP。我们一般都会使用一些互联网上提供的免费代理IP来进行爬取,而这些免费IP的产品质量参差不齐,错误在所难免,所以我们在使用之前需要测试其有效性。另外,对开源IP池感兴趣的同学可以在Github上了解开源项目:实战——爬取CSDN博客。原理导入urllib.request
def use_proxy(url,proxy_addr,iHeaders,timeoutSec):
"""
功能:伪装成浏览器,使用代理IP防止拦截
@url:目标网址
@proxy_addr:代理IP地址
@iHeaders:浏览器标头信息
@timeoutSec:超时设置(单位:秒)
"""
proxy=urllib.request.ProxyHandler({"http":proxy_addr})
opener=urllib.request.build_opener(代理,urllib.request.HTTPHandler)
urllib.request.install_opener(开启器)
尝试:
req=urllib.request.Request(url, headers=iHeaders) #伪装成浏览器,封装请求
data=urllib.request.urlopen(req).read().decode("utf-8","忽略")
除了er: 异常
print("爬取时发生错误,详细信息如下:")
打印机)
返回数据
url="http://www.baidu.com"
proxy_addr="125.94.0.253:8080"
iHeaders={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML,如Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0"}
超时秒=10
数据=use_proxy(url,proxy_addr,iHeaders,timeoutSec)
print(len(data))
4.9 抓包分析
IPProxyPool网站中用户所需的数据,例如联系人列表,可以从独立于实际网页的服务器获取,并可以动态写入网页中。简单来说,你打开一个网页,先显示部分内容,然后慢慢加载其余内容。显然,这样的网页加载速度非常快,因为它不需要一次性加载所有内容,但是对于我们的爬虫来说就比较麻烦了。我们不可能总能抓取到我们想要的内容。这时候我们就需要进行抓包分析。实战——代理服务器爬取百度首页推荐Fiddler和Chrome浏览器Ajax(异步加载)的技术请转至《5.2 爬取基于Ajax技术网页数据》。
4.10多线程爬虫
一般我们的程序都是单线程运行的,但是多线程可以充分利用资源,优化爬虫效率。其实Python中的多线程并行并不是真正的并行,但是多线程还是可以在一定程度上提高爬虫的执行效率。下面我们来对比一下单线程和多线程的时间。
抓包工具"""多线程"""
进口
urllib from multiprocessing.dummy import Pool import time def getResponse(url): """获取响应信息""" try: req = urllib.request.Request(url) res = urllib.request.urlopen(req) except Exception as er: print("爬取时发生错误,具体如下:") print(er) return res def getURLs(): """获取所需爬取的所有URL""" urls = [] for i in range(0, 101,20):#每翻一页其start值增加20 keyword = "科幻" keyword = urllib.request.quote(keyword) newpage = "https://movie.douban.com/tag/"+keyword+"?start="+str(i)+"&type=T" urls.append(newpage) return urls def singleTime(urls): """单进程计时""" time1 = time.time() for i in urls: print(i) getResponse(i) time2 = time.time() return str(time2 - time1) def multiTime(urls): """多进程计时""" pool = Pool(processes=4) #开启四个进程 time3 = time.time() pool.map(getResponse,urls) pool.close() pool.join() #等待进程池中的worker进程执行完毕 time4 = time.time() return str(time4 - time3) if __name__ == "__main__": urls = getURLs() singleTimes = singleTime(urls) #单线程计时 multiTimes = multiTime(urls) #多线程计时 print("单线程耗时 : " + singleTimes + " s") print("多线程耗时 : " + multiTimes + " s")结果:单线程耗时 : 3.850554943084717 s 多线程耗时 : 1.3288819789886475 s更多详情请参考:Python 并行任务技巧 Python中的多进程处理4.11 数据存储
本地文件(excel、txt)数据库(如MySQL)备注:具体实战请看5.14.12 验证码处理
在登录过程中我们常遇到验证码问题,此时我们有必要对其进行处理。简单验证码识别利用pytesser识别简单图形验证码,有兴趣的童鞋,请参考Python验证码识别。复杂验证码识别这相对有难度,可以调用第三方接口(如打码兔)、利用数据挖掘算法如SVM,有兴趣参考字符型图片验证码识别完整过程及Python实现5 综合实战案例
5.1 爬取静态网页数据
(1)需求
爬取豆瓣网出版社名字并分别存储到excel、txt与MySQL数据库中。(2)分析
查看源码Ctrl+F搜索任意出版社名字,如博集天卷确定正则模式"(.*?)"(3)思路
下载目标页面正则匹配目标内容Python列表存储写入Excel/txt/MySQL(4)源码
"""信息存储""" import urllib import re import xlsxwriter import MySQLdb #-----------------(1)存储到excel与txt-------------------------# def gxls_concent(target_url,pat): """ 功能:爬取数据 @target_url:爬取目标网址 @pat:数据过滤模式 """ data = urllib.request.urlopen(target_url).read() ret_concent = re.compile(pat).findall(str(data,"utf-8")) return ret_concent def wxls_concent(ret_xls,ret_concent): """ 功能:将最终结果写入douban.xls中 @ret_xls:最终结果存储excel表的路径 @ret_concent:爬取数据结果列表 """ # 打开最终写入的文件 wb1 = xlsxwriter.Workbook(ret_xls) # 创建一个sheet工作对象 ws = wb1.add_worksheet() try: for i in range(len(ret_concent)): data = ret_concent[i] ws.write(i,0,data) wb1.close() except Exception as er: print("写入“"+ret_xls+"”文件时出现错误") print(er) def wtxt_concent(ret_txt,ret_concent): """ 功能:将最终结果写入douban.txt中 @ret_xls:最终结果存储excel表的路径 @ret_concent:爬取数据结果列表 """ fh = open(ret_txt,"wb") try: for i in range(len(ret_concent)): data = ret_concent[i] data = data+"rn" data = data.encode() fh.write(data) except Exception as er: print("写入“"+ret_txt+"”文件时出现错误") print(er) fh.close() def mainXlsTxt(): """ 功能:将数据存储到excel表中 """ target_url = "https://read.douban.com/provider/all" # 爬取目标网址 pat = "(.*?)" # 爬取模式 ret_xls = "F:/spider_ret/douban.xls" # excel文件路径 ret_txt = "F:/spider_ret/douban.txt" # txt文件路径 ret_concent = gxls_concent(target_url,pat) # 获取数据 wxls_concent(ret_xls,ret_concent) # 写入excel表 wtxt_concent(ret_txt,ret_concent) # 写入txt文件 #---------------------END(1)--------------------------------# #-------------------(2)存储到MySQL---------------------------# def db_con(): """ 功能:连接MySQL数据库 """ con = MySQLdb.connect( host="localhost", # port user="root", # usr_name passwd="xxxx", # passname db="urllib_data", # db_name charset="utf8", local_infile = 1 ) return con def exeSQL(sql): """ 功能:数据库查询函数 @sql:定义SQL语句 """ print("exeSQL: " + sql) #连接数据库 con = db_con() con.query(sql) def gdb_concent(target_url,pat): """ 功能:转换爬取数据为插入数据库格式:[[value_1],[value_2],...,[value_n]] @target_url:爬取目标网址 @pat:数据过滤模式 """ tmp_concent = gxls_concent(target_url,pat) ret_concent = [] for i in range(len(tmp_concent)): ret_concent.append([tmp_concent[i]]) return ret_concent def wdb_concent(tbl_name,ret_concent): """ 功能:将爬取结果写入MySQL数据库中 @tbl_name:数据表名 @ret_concent:爬取数据结果列表 """ exeSQL("drop table if exists " + tbl_name) exeSQL("create table " + tbl_name + "(pro_name VARCHAR(100));") insert_sql = "insert into " + tbl_name + " values(%s);" con = db_con() cursor = con.cursor() try: cursor.executemany(insert_sql,ret_concent) except Exception as er: print("执行MySQL:"" + str(insert_sql) + ""时出错") print(er) finally: cursor.close() con.commit() con.close() def mainDb(): """ 功能:将数据存储到MySQL数据库中 """ target_url = "https://read.douban.com/provider/all" # 爬取目标网址 pat = "(.*?)" # 爬取模式 tbl_name = "provider" # 数据表名 # 获取数据 ret_concent = gdb_concent(target_url,pat) # 写入MySQL数据库 wdb_concent(tbl_name,ret_concent) #---------------------END(2)--------------------------------# if __name__ == "__main__": mainXlsTxt() mainDb()(5)结果
5.2 爬取基于Ajax技术网页数据
(1)需求
爬取拉勾网广州的数据挖掘岗位信息并存储到本地Excel文件中(2)分析
岗位数据在哪里?打开拉勾网==》输入关键词“数据挖掘”==》查看源码==》没发现岗位信息 打开拉勾网==》输入关键词“数据挖掘”==》按F12==》Network刷新==》按下图操作 我们可以发现存在position和company开头的json文件,这很可能就是我们所需要的岗位信息,右击选择open link in new tab,可以发现其就是我们所需的内容。如何实现翻页?我们在写爬虫的时候需要多页爬取,自动模拟换页操作。首先我们点击下一页,可以看到url没有改变,这也就是Ajax(异步加载)的技术。点击position的json文件,在右侧点击Headers栏,可以发现最底部有如下内容: 当我们换页的时候pn则变为2且first变为false,故我们可以通过构造post表单进行爬取。Json数据结构怎么样?(3)源码
import urllib.request import urllib.parse import socket from multiprocessing.dummy import Pool import json import time import xlsxwriter #----------------------------------------------------------# ### ###(1)获取代理IP ### def getProxies(): """ 功能:调用API获取原始代理IP池 """ url = "http://api.xicidaili.com/free2016.txt" i_headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0"} global proxy_addr proxy_addr = [] try: req = urllib.request.Request(url,headers = i_headers) proxy = urllib.request.urlopen(req).read() proxy = proxy.decode("utf-8") proxy_addr = proxy.split("rn") #设置分隔符为换行符 except Exception as er: print(er) return proxy_addr def testProxy(curr_ip): """ 功能:利用百度首页,逐个验证代理IP的有效性 @curr_ip:当前被验证的IP """ socket.setdefaulttimeout(5) #设置全局超时时间 tarURL = "https://www.baidu.com/" #测试网址 proxy_ip = [] try: proxy_support = urllib.request.ProxyHandler({"http":curr_ip}) opener = urllib.request.build_opener(proxy_support) opener.addheaders=[("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0")] urllib.request.install_opener(opener) res = urllib.request.urlopen(tarURL).read() proxy_ip.append(curr_ip) print(len(res)) except Exception as er: print("验证代理IP("+curr_ip+")时发生错误:"+er) return proxy_ip def mulTestProxies(proxies_ip): """ 功能:构建多进程验证所有代理IP @proxies_ip:代理IP池 """ pool = Pool(processes=4) #开启四个进程 proxies_addr = pool.map(testProxy,proxies_ip) pool.close() pool.join() #等待进程池中的worker进程执行完毕 return proxies_addr #----------------------------------------------------------# ### ###(2)爬取数据 ### def getInfoDict(url,page,pos_words_one,proxy_addr_one): """ 功能:获取单页职位数据,返回数据字典 @url:目标URL @page:爬取第几页 @pos_words_one:搜索关键词(单个) @proxy_addr_one:使用的代理IP(单个) """ global pos_dict page = 1 i_headers=("User-Agent","Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0") proxy = urllib.request.ProxyHandler({"http":proxy_addr_one}) opener = urllib.request.build_opener(proxy,urllib.request.HTTPHandler) opener.addheaders=[i_headers] urllib.request.install_opener(opener) if page==1: tORf = "true" else: tORf = "false" mydata = urllib.parse.urlencode({"first": tORf, "pn": page, #pn变化实现翻页 "kd": pos_words_one } ).encode("utf-8") try: req = urllib.request.Request(url,mydata) data=urllib.request.urlopen(req).read().decode("utf-8","ignore") #利用代理ip打开 pos_dict = json.loads(data) #将str转成dict except urllib.error.URLError as er: if hasattr(er,"code"): print("获取职位信息json对象时发生URLError错误,错误代码:") print(er.code) if hasattr(er,"reason"): print("获取职位信息json对象时发生URLError错误,错误原因:") print(er.reason) return pos_dict def getInfoList(pos_dict): """ 功能:将getInfoDict()返回的数据字典转换为数据列表 @pos_dict:职位信息数据字典 """ pos_list = [] #职位信息列表 jcontent = pos_dict["content"]["positionResult"]["result"] for i in jcontent: one_info = [] #一个职位的相关信息 one_info.append(i["companyFullName"]) one_info.append(i["companySize"]) one_info.append(i["positionName"]) one_info.append(i["education"]) one_info.append(i["financeStage"]) one_info.append(i["salary"]) one_info.append(i["city"]) one_info.append(i["district"]) one_info.append(i["positionAdvantage"]) one_info.append(i["workYear"]) pos_list.append(one_info) return pos_list def getPosInfo(pos_words,city_words,proxy_addr): """ 功能:基于函数getInfoDict()与getInfoList(),循环遍历每一页获取最终所有职位信息列表 @pos_words:职位关键词(多个) @city_words:限制城市关键词(多个) @proxy_addr:使用的代理IP池(多个) """ posInfo_result = [] title = ["公司全名", "公司规模", "职位名称", "教育程度", "融资情况", "薪资水平", "城市", "区域", "优势", "工作经验"] posInfo_result.append(title) for i in range(0,len(city_words)): #i = 0 key_city = urllib.request.quote(city_words[i]) #筛选关键词设置:gj=应届毕业生&xl=大专&jd=成长型&hy=移动互联网&px=new&city=广州 url = "https://www.lagou.com/jobs/positionAjax.json?city="+key_city+"&needAddtionalResult=false" for j in range(0,len(pos_words)): #j = 0 page=1 while page<10: #每个关键词搜索拉钩显示30页,在此只爬取10页 pos_words_one = pos_words[j] #k = 1 proxy_addr_one = proxy_addr[page] #page += 1 time.sleep(3) pos_info = getInfoDict(url,page,pos_words_one,proxy_addr_one) #获取单页信息列表 pos_infoList = getInfoList(pos_info) posInfo_result += pos_infoList #累加所有页面信息 page += 1 return posInfo_result #----------------------------------------------------------# ### ###(3)存储数据 ### def wXlsConcent(export_path,posInfo_result): """ 功能:将最终结果写入本地excel文件中 @export_path:导出路径 @posInfo_result:爬取的数据列表 """ # 打开最终写入的文件 wb1 = xlsxwriter.Workbook(export_path) # 创建一个sheet工作对象 ws = wb1.add_worksheet() try: for i in range(0,len(posInfo_result)): for j in range(0,len(posInfo_result[i])): data = posInfo_result[i][j] ws.write(i,j,data) wb1.close() except Exception as er: print("写入“"+export_path+"”文件时出现错误:") print(er) #----------------------------------------------------------# ### ###(4)定义main()函数 ### def main(): """ 功能:主函数,调用相关函数,最终输出路径(F:/spider_ret)下的positionInfo.xls文件 """ #---(1)获取代理IP池 proxies = getProxies() #获取原始代理IP proxy_addr = mulTestProxies(proxies) #多线程测试原始代理IP #---(2)爬取数据 search_key = ["数据挖掘"] #设置职位关键词(可以设置多个) city_word = ["广州"] #设置搜索地区(可以设置多个) posInfo_result = getPosInfo(search_key,city_word,proxy_addr) #爬取职位信息 #---(3)存储数据 export_path = "F:/spider_ret/positionInfo.xls" #设置导出路径 wXlsConcent(export_path,posInfo_result) #写入到excel中 if __name__ == "__main__": main()5.3 利用Scrapy框架爬取
5.3.1 了解Scrapy
Scrapy使用了Twisted异步网络库来处理网络通讯。整体架构大致如下(注:图片来自互联网): 详情转Scrapy:Python的爬虫框架 关于Scrapy的使用方法请参考官方文档5.3.2 Scrapy自动爬虫
前面的实战中我们都是通过循环构建URL进行数据爬取,其实还有另外一种实现方式,首先设定初始URL,获取当前URL中的新链接,基于这些链接继续爬取,直到所爬取的页面不存在新的链接为止。(1)需求
采用自动爬虫的方式爬取糗事百科文章链接与内容,并将文章头部内容与链接存储到MySQL数据库中。(2)分析
怎么提取首页文章链接?打开首页后查看源码,搜索首页任一篇文章内容,可以看到"/article/118123230"链接,点击进去后发现这就是我们所要的文章内容,所以我们在自动爬虫中需设置链接包含"article"怎么提取详情页文章内容与链接内容 打开详情页后,查看文章内容如下: 分析可知利用包含属性class且其值为content的div标签可唯一确定文章内容,表达式如下: "//div[@class="content"]/text()"链接打开任一详情页,复制详情页链接,查看详情页源码,搜索链接如下: 采用以下XPath表达式可提取文章链接。 ["//link[@rel="canonical"]/@href"](3)项目源码
创建爬虫项目
打开CMD,切换到存储爬虫项目的目录下,输入: scrapy startproject qsbkauto项目结构说明spiders.qsbkspd.py:爬虫文件items.py:项目实体,要提取的内容的容器,如当当网商品的标题、评论数等pipelines.py:项目管道,主要用于数据的后续处理,如将数据写入Excel和db等settings.py:项目设置,如默认是不开启pipeline、遵守robots协议等scrapy.cfg:项目配置创建爬虫
进入创建的爬虫项目,输入: scrapy genspider -t crawl qsbkspd qiushibaie=ke.com(域名)定义items
import scrapy class QsbkautoItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() Link = scrapy.Field() #文章链接 Connent = scrapy.Field() #文章内容 pass编写爬虫
qsbkauto.py# -*- coding: utf-8 -*- import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from qsbkauto.items import QsbkautoItem from scrapy.http import Request class QsbkspdSpider(CrawlSpider): name = "qsbkspd" allowed_domains = ["qiushibaike.com"] #start_urls = ["http://qiushibaike.com/"] def start_requests(self): i_headers={"User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.22 Safari/537.36 SE 2.X MetaSr 1.0"} yield Request("http://www.qiushibaike.com/",headers=i_headers) rules = ( Rule(LinkExtractor(allow=r"article/"), callback="parse_item", follow=True), ) def parse_item(self, response): #i = {} #i["domain_id"] = response.xpath("//input[@id="sid"]/@value").extract() #i["name"] = response.xpath("//div[@id="name"]").extract() #i["description"] = response.xpath("//div[@id="description"]").extract() i = QsbkautoItem() i["content"]=response.xpath("//div[@class="content"]/text()").extract() i["link"]=response.xpath("//link[@rel="canonical"]/@href").extract() return ipipelines.pyimport MySQLdb import time class QsbkautoPipeline(object): def exeSQL(self,sql): """ 功能:连接MySQL数据库并执行sql语句 @sql:定义SQL语句 """ con = MySQLdb.connect( host="localhost", # port user="root", # usr_name passwd="xxxx", # passname db="spdRet", # db_name charset="utf8", local_infile = 1 ) con.query(sql) con.commit() con.close() def process_item(self, item, spider): link_url = item["link"][0] content_header = item["content"][0][0:10] curr_date = time.strftime("%Y-%m-%d",time.localtime(time.time())) content_header = curr_date+"__"+content_header if (len(link_url) and len(content_header)):#判断是否为空值 try: sql="insert into qiushi(content,link) values(""+content_header+"",""+link_url+"")" self.exeSQL(sql) except Exception as er: print("插入错误,错误如下:") print(er) else: pass好了,文章到此结束,希望可以帮助到大家。
【网络爬虫入门教程(一)】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
爬虫这个东西听起来好厉害的样子!
有15位网友表示赞同!
想问问爬虫是怎么工作的?太好奇了。
有12位网友表示赞同!
爬虫是不是很耗CPU啊?
有9位网友表示赞同!
爬虫可以爬取网络上所有信息吗?
有12位网友表示赞同!
爬虫会不会被网站封IP?
有6位网友表示赞同!
有用爬虫爬取游戏数据,玩起来会更方便。
有20位网友表示赞同!
看论坛上有人说爬虫可以用 python 实现的吗?
有5位网友表示赞同!
学习爬虫感觉可以提高编程水平。
有15位网友表示赞同!
想用爬虫来采集一些新闻信息,看看能不能做个自动整理的功能。
有13位网友表示赞同!
听说爬虫需要学习网络协议?
有15位网友表示赞同!
爬虫有没有什么特别的安全风险需要注意?
有20位网友表示赞同!
现在很多网站都限制了爬取的频率吧?
有14位网友表示赞同!
想了解一下,常见的爬虫工具有哪些?
有18位网友表示赞同!
爬虫可以用来做数据分析吗?太棒了吧!
有18位网友表示赞同!
最近在学机器学习,爬虫会不会用到?
有16位网友表示赞同!
爬虫技术现在还有很强的应用价值吗?
有13位网友表示赞同!
爬虫的学习成本怎么样呢?对初学者友好吗?
有15位网友表示赞同!
想找些爬虫教程学习一下,请问哪里比较好?
有18位网友表示赞同!
用爬虫采集数据要注意版权问题吧?
有7位网友表示赞同!
用爬虫是不是会被网站认成恶意访问啊?
有13位网友表示赞同!