Spider简介:
可以分成简单的几步:抓取页面,分析页面和存储数据(主要用到的库有requests,Selenium,aiohttp)
解决JavaScrip渲染问题
- 分析Ajax请求
- Selenium/ WebDriver
- Splash
- PyV8、Ghost.py
解析方式
- 直接处理
- Json解析
- 正则表达式
- BeautifulSoup
- PyQuery
- XPath
爬取数据类型
- 网页文本:如HTML、JSON格式文本等
- 图片:获取到的是二进制文件,保存为图片格式
- 视频:同为二进制文件,保存为视频格式
- 其他:只要能请求到的,都能获取
保存数据
- 文本:纯文本、Json、Xml等
- 关系型数据库:如MySQL、Oracle、SQL Server等具有结构化表结构形式存储
- 非关系型数据库:MongoDB、Redis等Key-Value形式存储。
- 二进制文件:图片、视频、音频等直接保存成特定格式即可。
选择器:
-
网页由一个个节点组成, CSS选择器会根据不同的节点设置不同的样式规则:
- 在 css 中,使用 css 选择器来定位节点。 例: div 节点的 id 为 container,那么就可以表示为#container,其中#开头代表选择 id,其后紧跟 id 的名称。
-
如果想选择 class 为 wrapper 的节点,便可以使用.wrapper,这里以点(.)开头代表选择 class ,其后紧跟 class 的名称。
-
根据标签名筛选,例如想选择二级标题,直接用 h2 即可。 这是最常用的 3 种表示,分别是根据id、class、标签名筛选,请牢记它们的写法。
- CSS 选择器还支持嵌套选择,
- 各个选择器之间加上空格分隔开便可以代表嵌套关系, 如 #container .wrapper p 则代表先选择 id 为 container 的节点,然后选中其内部的 class 为 wrapper 的节 点,然后再进一步选中其内部的 p 节点。
- 如果不加空格,则代表并列关系,如 div#container .wrapper p .text 代表先选择 id 为 container 的 div 节点,然后选中其内部的 class 为 wrapper 的节点,再进一 步选中其内部的 class 为 text 的 p 节点。 这就是 css 选择器,其筛选功能还是非常强大的。
- CSS 选择器还有一些其他语法规则,具体如表 2-4 所示:
-
选择器 例子 例子描述 CSS .class .intro 选择 class="intro" 的所有元素。 1 #id #firstname 选择 id="firstname" 的所有元素。 1 * * 选择所有元素。 2 element p 选择所有 元素。
1 element,element div,p 选择所有 元素和所有元素。
1 element element div p 选择 元素内部的所有元素。
1 element>element div>p 选择父元素为 元素的所有元素。
2 element+element div+p 选择紧接在 元素之后的所有元素。
2 [attribute] [target] 选择带有 target 属性所有元素。 2 [attribute=value] [target=_blank] 选择 target="_blank" 的所有元素。 2 [attribute~=value] [title~=flower] 选择 title 属性包含单词 "flower" 的所有元素。 2 [attribute|=value] [lang|=en] 选择 lang 属性值以 "en" 开头的所有元素。 2 :link a:link 选择所有未被访问的链接。 1 :visited a:visited 选择所有已被访问的链接。 1 :active a:active 选择活动链接。 1 :hover a:hover 选择鼠标指针位于其上的链接。 1 :focus input:focus 选择获得焦点的 input 元素。 2 :first-letter p:first-letter 选择每个 元素的首字母。
1 :first-line p:first-line 选择每个 元素的首行。
1 :first-child p:first-child 选择属于父元素的第一个子元素的每个 元素。
2 :before p:before 在每个 元素的内容之前插入内容。
2 :after p:after 在每个 元素的内容之后插入内容。
2 :lang(language) p:lang(it) 选择带有以 "it" 开头的 lang 属性值的每个 元素。
2 element1~element2 p~ul 选择前面有 元素的每个
- 元素。
3 [attribute^=value] a[src^="https"] 选择其 src 属性值以 "https" 开头的每个 元素。 3 [attribute$=value] a[src$=".pdf"] 选择其 src 属性以 ".pdf" 结尾的所有 元素。 3 [attribute*=value] a[src*="abc"] 选择其 src 属性中包含 "abc" 子串的每个 元素。 3 :first-of-type p:first-of-type 选择属于其父元素的首个 元素的每个
元素。
3 :last-of-type p:last-of-type 选择属于其父元素的最后 元素的每个
元素。
3 :only-of-type p:only-of-type 选择属于其父元素唯一的 元素的每个
元素。
3 :only-child p:only-child 选择属于其父元素的唯一子元素的每个 元素。
3 :nth-child(n) p:nth-child(2) 选择属于其父元素的第二个子元素的每个 元素。
3 :nth-last-child(n) p:nth-last-child(2) 同上,从最后一个子元素开始计数。 3 :nth-of-type(n) p:nth-of-type(2) 选择属于其父元素第二个 元素的每个
元素。
3 :nth-last-of-type(n) p:nth-last-of-type(2) 同上,但是从最后一个子元素开始计数。 3 :last-child p:last-child 选择属于其父元素最后一个子元素每个 元素。
3 :root :root 选择文档的根元素。 3 :empty p:empty 选择没有子元素的每个 元素(包括文本节点)。
3 :target #news:target 选择当前活动的 #news 元素。 3 :enabled input:enabled 选择每个启用的 元素。 3 :disabled input:disabled 选择每个禁用的 元素 3 :checked input:checked 选择每个被选中的 元素。 3 :not(selector) :not(p) 选择非 元素的每个元素。
3 ::selection ::selection 选择被用户选取的元素部分。 3
爬虫的基本原理:
- 发起请求——> 获取响应内容——>解析内容——>保存数据
- 发起请求:通过HTTP库向目标站点发起请求,即发送一个Request,请求可以包含额外的Headers等信息,等待服务器响应
- 解析内容:得到的内容可能是HTML,通用正则表达式提取(万能方法)、网页解析库进行行解析。可能是Json,可以直接转为Json对象解析,可能是二进制数据,可以做保存或者进一步的处理。
- 获取响应内容:如果服务器能正常响应,会得到一个Response,Response的内容
- 保存数据:保存形式多样可以存为文本,也可以保存至数据库(MySQL和MongoDB),或者保存特定格式的文件(TEXT,JSON)
Request与Response
- 关系
- HTTP Request:浏览器发送消息给该网址所在的服务器
- HTTP Response:务器收到浏览器发送的消息后,能够根据浏览器发送的消息内容做相应处理,然后把消息回传给浏览器。
- 浏览器收到服务器的response信息后,会对信息进行相应处理,然后展示。
-
HTTP请求过程:
- Name: 请求的名称,一般会将URL的最后一部分内容当作名称
- Status:响应的状态码,显示200,代表响应正常。通过状态码,可以判断发送请求后是否得到正常的响应
- Type:请求的文档类型。这里问document,代表我们请求的是一个HTML文档,内容就是一些HTML代码
- Initiator:请求源。用来标记请求是由哪个对象或进程发起的
- Size:从服务器下载的文件和请求的资源大小。如果是从缓存中取的资源,则该列会显示from cache。
- Time:发起请求得到获取响应所用的总时间
- Waterfall:网络请求的可视化瀑布流。
- 请求(Request)
- 请求由客户端想服务端发出,可以分为4个部分:请求方法(Request Method)、请求的网址(Request URL)、请求头(Request Headers)、请求体(Request Body)
- 请求方法
- 请求方式:常见的主要有GET、POST两种类型,另外还有HEAD、PUT、DELETE、OPTIONS等
-
GET和POST请求方法主要区别。
-
GET请求中的参数包含在URL里面,数据可以在URL中看到,而POST请求的URL不会包含这些数据,数据都是通过表单形式传输的,会包含在请求体中。
-
GET请求提交的数据最多只有1024字节,而POST方式没有限制。
-
-
- 请求头:包含请求时的头部信息,如比较重要的信息有:Referer、User-Agent、Host、Cookies等信息
- Accept:请求报头域,用于指定客户端可接受哪些类型的信息。
- Accept-Language:指定客户端可接受的语言类型。
- accept-Encoding:指定客户端可接受的内容编码。
- Host:用于指定请求资源的主机IP和端口号,其内容为请求URL的冤死服务器或网关的位置。从HTTP1.1版本开始,请求必须包含此内容
- Cookie:常用复数形式Cookies,这是网站为了辨别用户进行绘画跟踪而存储在用户本地的数据。它主要的功能是维持当前访问会话。
- Referer:此内容用来标识这个请求是从哪个页面发过来的,服务器可以拿到这一信息并做相应的处理,如源统计,防盗链处理等
- User-Agent:简称UA,它是一个特殊的字符串头,可以使服务器识别客户使用的操作系统及版本,浏览器及版本等信息。他在做爬虫加上此次信息,可以伪装为浏览器;如果不加,可能会识别为爬虫
- Content-Type:也叫互联网媒体类型或者MIME类型,在HTTP协议消息头中,它用来表示具体请求中的媒体类型信息。如:text/html代表HTML格式,image/gif代表GIF图片,application/json代表JSON类型,更多对应关系可查看对照表:http://tool.oschina.net/commons
- 请求URL:URL全称统一资源定位符,如一个网页文档、一张图片、一个视频等都可以用URL唯一来确定
- 请求体:请求时额外携带的数据。如表单提交时的表单数据
- 只有设置Request Headers中指定Content-Type 为application/x-www-form-urlencoded,才会以表单数据的形式提交。另外,我们也可以将Content-Type设置为application/json来提交json数据,或者设置为multipart/form-data来上传文件。
- Content-Type和POST提交数据方式的关系
- application/x-www-form-urlencoded : 表单数据
- multipart/form-data :表单文件上传
- application/json :序列化JSON数据
- text/xml : XML数据
- 注意:爬虫中,如果要沟构造POST请求,需要使用正确的Content-Tpye,并了解各种请求库的各个参数设置时使用的是哪张Content-Type,不然会导致POST提交后无法正常响应
- 请求方式:常见的主要有GET、POST两种类型,另外还有HEAD、PUT、DELETE、OPTIONS等
- 响应(Response)
- 响应由服务端返回给客户端,可以分为三部分:响应状态码(Response Status Code)、响应头(Response Headers)和响应体(Response Body)
- Response
- 响应状态:有多种响应状态,如200代表成功、301跳转、4040找不到网页...
- 响应头:包含服务器对请求应答信息,如:content-type、server、set-cookies等。
- Date:标识响应产生的时间。
- Last-Modified:指定资源的最后修改时间
- Content-Encoding:指定响应内容的编码
- Server:包含服务器的信息,比如名称,版本号等。
- Content-Type:文档类型,指定返回的数据类型是什么,如text/html代表返回HTML文档,application/x-javascript则代表JavaScript文件,image/jpeg则代表返回图片。
- Set-Cookies:设置Cookies。响应头中的Set-Cookies告诉浏览器需要将此内容放在Cookies中,下次请求携带Cookies请求
- Expires:指定响应的过期时间,可以使代理服务器或浏览器将加在的内容更新到缓存中。如果再次访问时,就可以直接从缓存中加载,降低服务器负载,缩短加载时间。
- 响应体:最主要的部分,包含请求资源的内容,网页HTML、图片二进制数据等
- Cookies属性结构:
- Name:该Cookie的名称,一旦创建便不可更改
- Value:该Cookie的值。如果值为Unicode字符,需要为字符编码。如果值为二进制数据,则需要使用BASE64编码
- Domain:可以访问该Cookie的域名。例如,如果设置为.zhihu.com,则所有已zhihu.com结尾的域名都可以访问该Cookie
- Max Age: 该Cookie失效时间,单位为秒,也场合Expires一起使用,通过它可以计算出有效时间。Max Age如果为正数,则该Cookie在Max Age秒之后失效。如果为负数,则关闭浏览器时Cookie即失效,浏览器也不会以任何形式保存该Cookie
- Path:该Cookie的使用路径。如果设置为/Path/,则只有路径为/Path/的页面可以访问该Cookie。如果设置为/,则域名下的所有页面都可以访问该Cookie。
- Size字段:此Cookie的大小
- HTTP字段:Cookie的httponly属性。若此属性为true,则只有在HTTP头中会带有此Cookie信息,而不能通过document.cookie来访问此Cookie。
- Secure:该Cookie是否仅被使用安全协议传输。安全协议有HTTPS和SSL等,在网络上传输数据之前先将数据加密。默认为false。
- 会话Cookie:把Cookie放在浏览器内存里,浏览器在关闭之后该Cookie即失效
- 持久Cookie:把Cookie保存到客户端硬盘里,下次还可以继续使用,用于长久保持用户登录状态。
- 严格来讲,没有会话Cookie和持久cookie之分,只是由Cookie的Max Age或Expires字段决定过期时间。
代理的基本原理
- 代理分类
- 根据协议类型区分
- FTP代理服务器:主要用于访问FTP服务器,一般有上传、下载以及缓存功能,端口一般为21、2121等
- HTTP代理服务器:主要用于访问网页,一般有内容过滤和缓存功能,端口一般为80、8080、3128等
- SSL/TLS代理:主要用于访问加密网站,一般有SSL或TLS加密功能(最高支持128位加密强度),端口一般为443
- RTSP代理:主要用于访问R额按了流媒体服务器,一般有缓存功能,端口一般为554.
- Telnet代理:主要用于telnet远程控制(黑客入侵计算机时常用于隐藏身份),端口一般为23.
- POP3/STP代理:主要用于POP3/SMTP方式发邮件,一般有缓存功能,端口一般为110/25.
- SOCKS代理:只是单纯传递数据包,不关心具体协议和用法,所以速度快很多,一般有缓存功能,端口一般为1 0 8 0。SOCKS代理协议又分为SOCKS4和SOCKS5,前者只支持TCP ,而后者支持TCP和UDP,还支持各种身份验证机制、服务器端域名解析等。简单来说,SOCKS4能做到的SOCKS5都可以做到,但SOCKS5能做到的SOCKS4不一定能做到
- 根据匿名程度区分
- 高度匿名代理:会将数据包原封不动地转发,在服务端看来就好像真的是一个普通客户端在访问,而记录的IP是代理服务器的IP
- 普通匿名代理:会在数据包上做一些改动,服务端上有可能发现这是个代理服务器,也有一定几率追查到客户端的真实IP。代理服务器通常会加入的HTTP头有HTTP_VIA和HTTP_X_FORWARDED_FOR。
- 透明代理:不但改动数据包,还会告诉服务器客户端的真实IP。这种代理除了能用缓存技术提高浏览速度,能用内容过滤提高安全性之外,并无其他显著作用。最常见的例子是内网中的硬件防火墙
- 间谍代理:值组织或个人创建的用于记录用户传输的数据,然后进行研究、监控等目的的代理服务器。
- 根据协议类型区分
- 常见代理设置
- 使用网上的免费代理:最好使用高匿名代理,另外可用的代理不多,需要在使用前筛选一下可用代理,也可以进一步维护一个代理池
- 使用付费代理服务:互联网上存在许多代理商,可以付费使用,质量比免费代理好很多。
- ADSL拨号:拨一次号换一次IP,稳定性高,也是一种比较有效的解决方案。
三章:基本库的使用
- 使用urllib
- urllib是python 内置的HTTP请求库,包含4个模块。库官方文档:https://docs.python.org/3/library/urllib.html
- request:是最基本的HTTP请求模块,可以用来模拟发送请求。只需要给库方法传入URL以及额外的参数
- error:异常处理模块,如果出现请求错误,可以捕获这些一场,进行重试或其他操作以保证长须不会意外终止。
- parse:一个工具模块,提供许多URL处理方法,如:拆分/解析/合并等
- robotparser:主要用来识别网站的robots.txt文件,然后判断哪些网站可以爬,哪些网站不可以爬,使用比较少。
- 发送请求
- 使用urllib的request模块,可以方便地实现请求的发送并得到响应
- urlopen
- urlopen()方法用法,官方文档:https://docs.python.org/3/library/urllib.request.html
- urllib.request 模块提供最基本的构造HTTP请求的方法,利用他可以模拟浏览器的一个请求发起过程,同时还带有处理授权验证、重定向、浏览器Cookies以及其他内容
import urllib.request response = urllib.request.urlopen('https://www.python.org') print(response.read().decode('utf-8')) print(type(response)) 输出类型为<class 'http.client.HTTPResponse'>
- 以上内容是一个HTTPResponse类型对象,主要包含read()、readinto()、getheader(name)、getheaders()、fileno()等方法,以及msg、version、status、reason、debuglevel、close等属性。得到这个对象后,把它赋值为response变量,然后就可以调用这些方法属性,得到返回结果的一系列信息。
- 例:调用read()方法可以德奥犯规的网页内容,调用status属性可以得到返回状态码(200代表请求成功,404代表网页未找到等)
- 如果想给链接传递一些参数,如何实现,urlopen()函数的API:
- urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None), 意味着除了可以传递URL之外,还可以传递其他内容,比如data(附加数据)、timeout(时间设置)等,
- data参数
- data参数是可选的,如果需要添加,并且如果它是字节流编码格式的内容,即bytes类型,则需要通过bytes()方法转化。另外,如果传递这个参数,则他的请求方式就不在是Get方式,而是POTS方式。例:
import urllib.parse import urllib.request data = bytes(urllib.parse.urlencode({'word':'hello'}),encoding = 'utf-8') response = urllib.request.urlopen('http://httpbin.org/post',data= data) print(response.read())
这里我们传递一个参数word,值是hello。需要被转码成bytes(字节流)类型。其中转字节流采用了bytes()方法,该方法的第一个参数需要是str(字符串)类型,需要用urllib.parse模块里的urlencode()方法来将参数字典转化为字符串。 这里请求的站点是httpbin.org,他可以提供HTTP请求测试。本次的链接中http://httpbin.org/post可以用来测试POST请求,可以输出请求的一些信息,其中包含我们传递的data参数。 我们传递的参数出现在form字段中,是表明模拟表单提交方式,以POST方式传输数据
- data参数是可选的,如果需要添加,并且如果它是字节流编码格式的内容,即bytes类型,则需要通过bytes()方法转化。另外,如果传递这个参数,则他的请求方式就不在是Get方式,而是POTS方式。例:
- timeout参数
- timeout参数用于设置超时时间,单位为秒。它支持HTTP,HTTPS,FTP请求
- 其他参数
- context参数,它必须是ssl.SSLContext类型,用来指定SSL设置
- cafile和capath参数,分别指定CA证书和它的路径,这个在请求HTTPS链接时会有用
- cadefault参数默认False,已弃用
- data参数
- urllib.request.urlopen(url,data=None,[timeout,]*,cafile=None,capath=None,cadefault=False,context=None), 意味着除了可以传递URL之外,还可以传递其他内容,比如data(附加数据)、timeout(时间设置)等,
- Request
- 例:
import urllib.request request = urllib.request.Request('https://python.org') response = urllib.request.urlopen(request) print(response.read().decode('utf-8'))
依然使用urlopen()方法来发送请求,这次该方法的参数不再是URL,而是一个Request类型的对象。通过构造这个数据结构,一方面可以将请求独立成一个对象,另一方面可以更加丰富和灵活配置参数。
- Request的参数构造
- class urllib.request.Request(url, data=None, headers={}, origin_req_host=None,unverifiable=False,methond=None)
- url参数:用于请求URL,这是必传参数,其他都是可选参数。
- data参数:可选传参数,如果传必须要是bytes(字节流)类型的。如果它是字典,可以先用urllib.parse模块里的urlencode()编码。
- headers参数:是一个字典,它就是请求头,可以在构造请求时通过headers参数直接构造,也可以通过调用请求实例的add_header()方法添加。 添加最常用的方法就是修改User-Agent来伪装浏览器,默认的User-agent是Python-urllib,可以通过修改它来伪装。
- 例:伪装火狐浏览器,可设置为: Mozilla/5.0(X11; u; Linux i686) Gecko/20071127 Firefox/2.0.0.11
- origin_req_host参数:请求方的host名称或者IP地址
- unverifiable参数:表示这个请求是否无法验证的,默认是False。意思就是说用户没有足够权限来选择接受这个请求的结果
- 例:请求一个HTML文档中的图片,但是没有自动抓取图像的权限,这时候unverifiable的值就是True。
- method参数:是一个字符串,用来指示请求使用的方法,如GET、POST和PUT等。
- 传入多个参数构建请求:
from urllib import request,parse url='https://httpbin.org/post' headers={'User-Agent':'Mozilla/4.0(com[atible;MSIE 5.5; Windows NT)', 'host':'httpbin.org'} dict={'name':'Germey'} data = bytes(parse.urlencode(dict),encoding='utf-8') req=request.Request(url=url,data=data,headers= headers,method = 'POST') response=request.urlopen(req) print(response.read().decode('utf-8'))
其中headers指定User-Agent和Host,参数data用urlencode()和bytes()方法转成字节流。另外指定请求方式为POST。
- 另外headers也可以用add_header()方法来添加:
req= request.Request(url=url,datd= data, method='POST) req.add_headers('User-Agent','Mozilla/4.0(compatible; MSIE 5.5; Windows NT)')
- class urllib.request.Request(url, data=None, headers={}, origin_req_host=None,unverifiable=False,methond=None)
- Request的参数构造
- 例:
-
- 3.高级用法
-
-
- urllilb.request模块里的BaseHandler类,是所有其他Handler的父类,它提供最基本的方法,例:default_open()、protocol_request等(各种Handler子类继承BaseHandler类举例)
- HTTPDefaultErrorHandler:用于处理HTTP响应错误,错误都会抛出HTTPError类型的异常。
- HTTPRedirectHandler:用于处理重定向
- HTTPCookieProcessor:用于处理Cookies
- ProxyHandler:用于设置代理,默认代理为空
- HTTPPasswordMgr:用于管理密码,它维护了用户名和密码的表
- HTTPBasicAuthHandler:用于管理认证,如果一个连接打开时需要认证,那么可以用它来解决认证问题
- 其他Handler类,参考官方文档:https://docs.python.org/3/library/urllib.request.html#urllib.request.BaseHandler
- 另一个比较重要的类OPenerDirector,可以称为Opener。Opener可以使用open()方法,返回类型和urlopen()如出一辙。就是利用Handler来构建Opener().
- 验证
- 部分网站打开需要你输入用户名和密码,验证成功才能查看页面,如果请求这样页面。借助HTTPBasicAuthHandler可以完成,相关代码:
from urllib.request import HTTPPasswordMgrWithDefaultRealm,HTTPBasicAuthHandler,build_opener from urllib.error import URLError username = 'username' password='password' url='http://localhost:5000/' p=HTTPPasswordMgrWithDefaultRealm() p.add_password(None,url,username,password) auth_handler=HTTPBasicAuthHandler(p) opener=build_opener(auth_handler) try: result=opener.open(url) html=result.read().decode('utf-8') print(html) except URLError as e: print(e.reason)
首先实例化HTTPBasicAuthHandler对象,其参数HTTPPasswordMgrWithDefaultRealm对象,它利用add_password()添加进去用户名和密码,这样就能建立一个处理验证的Handler。 接下来,利用这个Handler并使用build_opener()方法构建一个Opener,这个Opener在发送请求时就相当于已经验证成功。 利用Opener()的open方法打开链接就可以完成验证。 这里获取到的结果就是样后的页面码源内容。
- 部分网站打开需要你输入用户名和密码,验证成功才能查看页面,如果请求这样页面。借助HTTPBasicAuthHandler可以完成,相关代码:
- 代理
- 爬虫时候丢需要代理,如果要添加代理,可如下:
from urllib.request import ProxyHandler,build_opener from urllib.error import URLError proxy_handler=ProxyHandler({ 'http':'http://127.0.0.1:9743', 'https':'https://127.0.0.1:9743' }) opener=build_opener(proxy_handler) try: response =opener.open('https://www.baidu.com') print(response.read().decode('utf-8')) except URLError as e: print(e.reason)
在本地搭建一个代理,运行在9734端口上。 这里是有ProxyHandler,其参数是一个字典,键名是协议类型(例:HTTP或HTTPS等),键值是代理链接,可添加多个代理。 然后利用这个Handler及build_opener()方法构造一个Opener,之后发送请求即可。
- 爬虫时候丢需要代理,如果要添加代理,可如下:
- Cookies
- Cookie的处理需要相关的Handler,例:
import http.cookiejar,urllib.request cookie =http.cookiejar.CookieJar() handler =urllib.request.HTTPCookieProcessor(cookie) opener = urllib.request.build_opener(handler) response =opener.open('http://www.baidu.com') for item in cookie: print(item.name+"="+item.value)
首先,我们必须声明一个cookieJar对象。接下来,需要利用HTTPCookieProcessor构建一个Handler,再利用build_opener()方法构建出Opener,执行open()函数即可。这里会输出每条cookie的名称和值
- Cookies实际上也是以文本形式保存的,既然能输出,也就可以输出成文件格式。例:
import http.cookiejar,urllib.request filename='cookies.txt' cookie= http.cookiejar.MozillaCookieJar(filename) handler=urllib.request.HTTPCookieProcessor(cookie) opener=urllib.request.build_opener(handler) response=opener.open('http://www.baidu.com') cookie.save(ignore_discard=True,ignore_expires= True)
这时CookieJar就需要换成MozillaCookieJar,它在生成文件时会用到,是CookieJar的子类,可以用来处理Cookies和文件相关的事件,比如读取和保存Cookies,可以将Cookies保存成Mozilla型浏览器的Cookies格式。运行后可以生成一个cookies.txt文件。 另外LWPcookieJar同样可以读取和保存Cookies,但是保存的格式和MozillaCookieJar不一样,会保存成libwww-perl(LWP)格式的cookies文件。可在声明时改为:
cookie = http.cookiejar.LWPCookieJar(filename)
- 读取cookie(LWPCookieJar格式为例):
import http.cookiejar,urllib.request cookie= http.cookiejar.LWPCookieJar() cookie.load('cookies.txt',ignore_discard=True,ignore_expires=True) handler=urllib.request.HTTPCookieProcessor(cookie) opener=urllib.request.build_opener(handler) response=opener.open('http://www.baidu.com') print(response.read().decode('utf-8'))
在此处调用load()方法来读取本地的Cookies文件,获取到Cookies内容。前提我们生成LWPCookieJar格式的Cookies文件。然后读取Cookies之后使用同一的方法构建Handler和Opener。 运行结果正常会输出百度页面的源代码。
- urllib库的request模块基本使用方法,更多可参考官方文档:https://docs.python.org/3/library/urllib.request.html#basehandler-objects。
- Cookie的处理需要相关的Handler,例:
- 验证
- urllilb.request模块里的BaseHandler类,是所有其他Handler的父类,它提供最基本的方法,例:default_open()、protocol_request等(各种Handler子类继承BaseHandler类举例)
-
- 2.处理异常
- urllib的error模块定义了由request模块产生的异常,如果出现问题,request模块便会抛出error模块中定义的异常
- URLError
- URLError类来自urllib库的error模块,它继承自OSError类,是error异常模块的基类,由request模块生成的异常都可以通过捕捉这个类来处理。 它具有一个reason,即返回错误的原因。例:
from urllib import request,error try: response=request.urlopen('https://cuiqingcai.com/index.htm') except error.URLError as e: print(e.reason)
打开不存在的页面,这时候捕获URLError这个异常,而没有报错。这样可以避免程序异常终止,同时异常得到有效处理。
- URLError类来自urllib库的error模块,它继承自OSError类,是error异常模块的基类,由request模块生成的异常都可以通过捕捉这个类来处理。 它具有一个reason,即返回错误的原因。例:
- HTTPError
- 它是URLError的子类,专门处理HTTP请求错误,如认证失败等。有如下3个属性
-
- Code:返回HTTP状态码,如404表示网页不存在,500表示服务器内部错误等。
- Reason:同父类一样,用于返回错误原因。
- headers:返回请求头。
- 例:
from urllib import request,error try: response=request.urlopen('https://cuiqingcai.com/index.htm') except error.HTTPError as e: print(e.reason,e.code,e.heanders)
同样的网址,这里捕获的是HTTPError异常,输出reason、code和headers属性。 因为URLError是HTTP的父类,所以这里先选择捕获子类错误,再捕获父类错误,故代码更新如下:
from urllib import request,error try: response=request.urlopen('https://cuiqingcai.com/index.htm') except error.HTTPError as e: print(e.reason,e.code,e.heanders) except error.URLError as e: print(e.reason) else: print('Request Successfully')
这样就可以先捕获HTTPError,获取它错误状态码、原因、headers等信息。如果不是HTTPError异常,就会捕获URLError异常,输出错误原因。 有时候,reason属性返回的不一定是字符串,也可能是一个对象。例:
import socket import urllib.request import urllib.error try: response= urllib.request.urlopen('https://www.baidu.com',timeout=0.01) except urllib.error.URLError as e: print(type(e.reason)) if isinstance(e.reason,socket.timeout): print('TIME OUT')
这里直接设置超时来强制抛出timeout异常。reason属性的结果是socket.timeout类。所以,这可以用isinstance()方法来判断它的类,做出更详细的异常判断。
- 3.解析链接
- urlparse()
- 该方法可以实现URL的识别和分段,例:
from urllib.parse import urlparse result= urlparse('http://www.baidu.com/index.html;user?id=5#comment') print(type(result),result)
这里利用urlparse()方法进行一个URL解析。返回结果是一个ParseResult类型对象,包含6个部分,分别是scheme、netloc、path、params、query和fragment。 该实例的URL: http://www.baidu.com/index.html;user?id=5#comment urlparse()方法将其拆分成6个部分。解析有特定的分割符。 :// 前面的就是scheme,代表协议; 第一个/符号前面便是netloc,即域名,后面是path,即访问路径;分号;前面是params,代表参数;问好?后面是查询条件query,一般用作GET类型的URL;#后面是锚点,用于直接定位页面内部的下拉位置。 可以得出一个标准的链接格式:scheme://netloc/path;params?query#fragment 一个标准的URL都会符合这个规则,利用urlparse()方法可以将它拆分开。
- urlparse的API用法:urllib.parse.urlparse(urlstring,scheme=' ',allow_fragments=True), ,它有3个参数。
- urlstring:必填项,待解析的URL
- scheme:默认协议(如http或https等)。假设这个链接没有带协议信息,会将这个作为默认协议。例:
from urllib.parse import urlparse result = urlparse('www.baidu.com/index.html;user?id=5#comment',scheme='https')
运行结果:ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment'),所提供的URL没有包含前面的scheme信息,但通过制定默认的scheme参数,返回结果是https。 如果带上scheme: result = urlparse('http://www.baidu.com/index.html;user?id=5#comment',scheme='https'), 结果就会返回解析出scheme带有的信息http
- allow_fragments:即是否忽略fragment。如果被设置为False,fragment部分就会被忽略,他会被解析为path、parameters或者query的一部分,而fragment部分为空。例:
from urllib.parse import urllib result =urlparse('http://www.baidu.com/index.html;user?id=5#comment',allow_fragments=False) print(result)
运行结果:
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5#comment', fragment='')
如果URL中不包含params和query,例:
from urllib.parse import urlparse result =urlparse('http://www.baidu.com/index.html#comment',allow_fragments=False) print(result)
运行结果:
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html#comment', params='', query='', fragment='')
可发现,当URL中不包含params和query时,fragment便会被解析为path的一部分。 返回结果ParseResult实际上是一个元组,可以通过索引顺序来获取,也可以用属性名获取。例:
from urllib.parse import urlparse result = urlparse('http://www.baidu.com/index.html#comment',allow_fragments=False) print(result.scheme,result[0],result.netloc,result[1])
这里,分别用了索引和属性名获取scheme和netloc。输出结果与一致:
http http www.baidu.com www.baidu.com
- urlparse的API用法:urllib.parse.urlparse(urlstring,scheme=' ',allow_fragments=True), ,它有3个参数。
- 该方法可以实现URL的识别和分段,例:
-
urlunparse()
-
有了urlparse()就会有对立方法urlunparse()。它接受的参数是一个可迭代对象,但长度必须是6,否则会抛出参数数量不足或过多问题.例:
from urllib.parse import urlunparse data = ['http','www.baidu.com','index.html','user','a=6','comment'] print(urlunparse(data)) 输出结果: http://www.baidu.com/index.html;user?a=6#comment
这里参数使data使用列表类型,也可以使用其他类型,比如元组或特定的数据结构。这样就可以成功是此案URL构造
-
-
urlsplit()
-
此方法和urlparse()相似。例:
from urllib.parse import urlsplit result=urlsplit('http://wwww.baidu.com/index.html;user?id=5#comment') print(result)
运行结果:SplitResult(scheme='http', netloc='wwww.baidu.com', path='/index.html;user', query='id=5', fragment='comment')
运行结果是SplitResult,也是一个元组类型,既可以用属性获取,也可以使用索引获取。例:
from urllib.parse import urlsplit result=urlsplit('http://wwww.baidu.com/index.html;user?id=5#comment') print(result.scheme,result[0]) 输出: http http
-
- 与urlunsplit()
- 与urlunparse类似,它也是讲链接各个部分组合完成完整链接的方法,传入的参数也是一个可迭代对象,例如列表、元组,唯一的区别是长度必须为5。示例如下:
from urllib.parse import urlunsplit data =['http','www.baidu.com','index.html','a=6','comment'] print(urlunsplit(data)) 输出: http://www.baidu.com/index.html?a=6#comment
- 与urlunparse类似,它也是讲链接各个部分组合完成完整链接的方法,传入的参数也是一个可迭代对象,例如列表、元组,唯一的区别是长度必须为5。示例如下:
-
urljoin()
-
有urlunparse()和urlunsplit()方法,就可以完成链接的合并,前提必须要有特定长度对象,链接的每一部分都要清晰分开。 此外,生成链接还有另外一种方法,urljoin()方法。可以提供一个base_url(基础链接)作为第一个参数,将新的链接作为第二参数,该方法会分析base_url的scheme、netloc和path这3个内容并对新链接缺失的部分进行补充,最后返回结果。例:
from urllib.parse import urljoin print(urljoin('http://www.baidu.com','FAQ.html')) >>>http://www.baidu.com/FAQ.html print(urljoin('http://www.baidu.com','https://cuiqingcai.com/FAQ.html')) >>> https://cuiqingcai.com/FAQ.html print(urljoin('http://www.baidu.com/about.html','https://cuiqingcai.com/FAQ.html?question=2')) >>> https://cuiqingcai.com/FAQ.html?question=2 print(urljoin('http://www.baidu.com?wd=abc','https://cuiqingcai.com/index.php')) >>> https://cuiqingcai.com/index.php
base_url提供三项内容scheme、netloc和path。如果这3项在新链接里不存在,就予以补充;如果新链接存在,就是要新链接部分。而base_url中的parms、query和fragment是不起作用的。
-
通过urljoin()方法,可以轻松实现链接的解析、拼合与是生成。
-
-
urlencode()
-
urlencode()在构造GET请求参数的时候非常有用,例:
from urllib.parse import urlencode params = { 'name':'germey','age':22} base_url ='http:www.baidu.com?' url=base_url+urlencode(params) print(url) 输出:
http:www.baidu.com?name=germey&age=22这里首先声明一个字典来将参数表示出来,然后调用urlencode()方法将其序列化为GET请求参数。 可得到,参数成功滴有字典类型转化为GET请求参数了。
-
-
parse_qs()
-
反序列化。如果有一串GET请求参数,利用parse_qs()方法,就可以将它转回字典。例:
from urllib.parse import parse_qs query = 'name=germey&age=22' print(parse_qs(query)) 输出: {'name': ['germey'], 'age': ['22']}
这样就可以成功转回为字典类型了。
-
- parse_qsl()
- parse_qsl()方法用于将参数转化为元组组成的列表,例:
from urllib.parse import parse_qsl query ='name=germey&age=22' print(parse_qsl(query)) 输出: [('name', 'germey'), ('age', '22')]
运行结果是一个列表,而列表中的每个元素都是一个元组,元组的第一个内容是参数名,第二个内容是参数值。
- parse_qsl()方法用于将参数转化为元组组成的列表,例:
- quote()
- 该方法可以将内容转化为URL编码格式。URL中带有中文参数时,有时候会导致乱码问题,此时用quote()这个方法可以将中文字符转化为URL编码。例:
from urllib.parse import quote keyword='壁纸' url= 'https://www.baidu.com/s?wd='+quote(keyword) print(url) 输出: https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8
这里声明一个中文的搜索文字,然后用quote()方法对其进行URL编码。
- 该方法可以将内容转化为URL编码格式。URL中带有中文参数时,有时候会导致乱码问题,此时用quote()这个方法可以将中文字符转化为URL编码。例:
- unquote()
- unquote()方法可以进行URL解码。例:
from urllib.parse import unquote url='https://www.baidu.com/s?wd=%E5%A3%81%E7%BA%B8' print(unquote(url)) 输出: https://www.baidu.com/s?wd=壁纸
上面得到URL编码后的结果,可以利用unquote()方法进行还原,解码。
- unquote()方法可以进行URL解码。例:
- urlparse()
- 4.分析Robots协议
- 利用urllib的robotparser模块,可以实现网站Robots协议的分析
- Robots协议
- Robots协议也叫爬虫协议、机器人协议,用来告诉爬虫和搜索引擎哪些页面可以爬取。通常是一个叫robots。txt的文本文件,存放在网站的根目录下。 当爬虫访问一个站点的时,会先检查这个站点根目录下是否穿在robots.txt文件,如果存在,爬虫会根据其中定义的范围来爬取。如果没找到这个文件,搜索爬虫变回访问所以可直接访问页面。 例一个robots.txt样例:
User-agent:* Disallow:/ Allow:/public/
这实现对所有爬虫只允许爬取public目录功能,将上诉内容保存成robots.txt文件,放在网站的根目录下,和网站的入口文件(如index.php、index.html和index.jsp)放一起。
- 上面的User-agent描述搜索爬虫的名称,这里将其设置为*代表协议对任何爬取爬虫都有效。例,可设置: User-agent:Baiduspider 这就代表我们设置的规则对百度爬虫是有效的。如果头多条User-agent记录,则代表会有多个爬虫收到爬取限制,但至少需要指定一条。
- Disallow指定不允许抓取的目录,比如上例子中设置为/则代表不允许抓取所有页面。
- Allow一般和Disallow一起使用,一般不会单独使用,用来排除某些限制。现在设置为/public/,则表示所有页面不允许爬取,但可以抓取public目录。
- 禁止所有爬虫访问任何目录的代码如下:
User-agent Disallow:/
允许所有爬虫访问任何目录的代码如下:
User-agent:* Disallow:
另外把robots.txt文件留空也是可以的。
-
禁止所有爬虫访问网站某些目录的代码:
User-agent:* Disallow:/private/ Disallow:/tmp/
只允许某一个爬虫访问的代码如下:
User-agent:WebCrawler Disallow: User-agent:* Disallow:/
- 禁止所有爬虫访问任何目录的代码如下:
- Robots协议也叫爬虫协议、机器人协议,用来告诉爬虫和搜索引擎哪些页面可以爬取。通常是一个叫robots。txt的文本文件,存放在网站的根目录下。 当爬虫访问一个站点的时,会先检查这个站点根目录下是否穿在robots.txt文件,如果存在,爬虫会根据其中定义的范围来爬取。如果没找到这个文件,搜索爬虫变回访问所以可直接访问页面。 例一个robots.txt样例:
- 爬虫名称
- Robotparser
- 在了解robots协议后,就可以使用robotparser模块来解析robots.txt.该模块提供一个类RobotFileParser,它可以根据网站的robots.txt文件来判断一个爬取爬虫是否有权限来爬取这个网页。
- 该类使用起来只需要在构造方法里传入robots.txt的链接即可。它的声明:urllib.robotparser.robotFileParser(url=' ')
- 当然,也可以在声明时不传入,默认为空,最后再使用set_url()方法设置一下也可。下面列出这个类常用几个方法:
- set_url():用来设置robots.txt文件的链接。如果创建RobotFileParser对象时传入了链接,那么就不需要再使用这个方法链接
- read():读取robots.txt文件并进行分析。注意:这个方法执行一个读取和分析操作,如果不调用这个方法,接下来判断都为False,所以一定需要调用。它不会返回任何内容,但执行读取操作。
- parse():用来解析robots.txt文件,传入的参数是robots.txt某些行的内容,他会按照robots.txt的语法规则来分析这些内容
- can_fetch():该方法传入两个参数,第一个是User-agent,第二个是要抓取的URL。返回的内容是该搜索引擎是否可以抓取这个URL,返回结果是True或Flase
- mtime():返回的是上次抓取和分析robots.txt的时间,这对于长时间分析和抓取的搜索爬虫是很有必要,需要定期检查来抓取最新的robots.txt
- modified():它同样对长时间分析和抓取的搜索爬虫有帮助,将当前时间设置为上次抓取和分析robots.txt的时间
- 例:
from urllib.robotparser import RobotFileParser rp = RobotFileParser() rp.set_url('http://www.jianshu.com/robots.txt') rp.read() print(rp.can_fetch('*','http://www.jianshu.com/p/b67554025d7d')) print(rp.can_fetch('*','http://www.jianshu.com/search?q=python&page=1&type=collections'))
这里以简书为例,首先创建RobotFileParser对象,然后通过set_url()方法设置robots.txt的链接。或者可以在声明时候直接用:rp=RobotFileParser('http://www.jianshu.com/robots.txt'), 接着使用can_fetch()方法判断网页是否被抓取。
- 同样也可以使用parse()方法执行读取和分析,示例:
from urllib.robotparser import RobotFileParser from urllib.request import urlopen rp = RobotFileParser() rp.parse(urlopen('http://www.jianshu.com/robots.txt').read().decode('utf-8').split('\n')) print(rp.can_fetch('*','http://www.jianshu.com/p/b6755402Sd7d')) print(rp.can_fetch('*',"http://www.jianshu.com/search?q=python&page=l&type=collections"))
- 例:
- urllib是python 内置的HTTP请求库,包含4个模块。库官方文档:https://docs.python.org/3/library/urllib.html
- 使用requests
- 基本用法
- 实例
- urllib库中的urlopen()方法实际上是以GET方式请求网页,而requests中相应的方法就是get()方法。例:
import requests r=requests.get('https://www.baidu.com/') print(type(r)) >>> <class 'requests.models.Response'> print(r.status_code) >>> 200 print(type(r.text)) >>> <class 'str'> print(r.text) >>> .......
- urllib库中的urlopen()方法实际上是以GET方式请求网页,而requests中相应的方法就是get()方法。例:
- 实例
- 基本用法