python网络爬虫笔记-urllib

写在前面:

这算是我的第一篇博客。我写博客的原因应该会和大部分人一样,为了交流和分享知识,同时也可以结交志同道合的朋友。由于我大四毕设的课题与网络爬虫相关,当时花了不少心思去学这一门技术,虽然不能说算是精通,但也是掌握了一些技巧和方法。同时呢,我当时也整理了很多的笔记,本来是没想着要写进博客里,不过最后还是决定行动了。这些笔记我会以文章的形式来展示出来,编程语言为python,大概5-6篇,其中会有我在进行毕设时遇到的一些问题,我也会一一说明,话不多说,一睹为快。(笔记里的一些网址如果说大家访问不了的话可以尝试挂vpn,每一个网址我都会自己去试验一下的,不会出现访问不了的情况)


一、爬虫基本原理

爬虫基本流程

1.发起请求。通过HTTP库向目标站点发起请求,即发送一个Request,请求可以包含额外的headers等信息,等待服务器响应。

2.获取响应内容。如果服务器能正常响应,会得到一个Response,Response的内容便是所要获得的页面内容,类型可能有HTML,JSON字符串,二进制数据(如图片视频)等类型。

3.解析内容。得到的内容可能是HTML,可以用正则表达式、网页解析库进行解析。可能是JSON,可以直接转为JSON对象解析,可能是二进制数据,可以保存或者进一步的处理。

4.保存数据。保存形式多样,可以存为文本,也可以保存至数据库,或者保存特定格式的文件。
在这里插入图片描述

Request

1.请求方式。主要有GET、POST两种类型,另外还有HEAD、PUT、DELETE、OPTIONS等。Post信息在URL中无显示,get信息参数全都位于URL中。

2.请求URL。URL全称统一资源定位符,如一个网页文档、一张图片、一个视频等都可以用URL唯一来确定 。

3.请求头。包含请求时的头部信息,如User-Agent、Host、Cookies等信息。

4.请求体。请求时额外携带的数据如表单提交时的表单数据。

在这里插入图片描述

Response

1.响应状态。200指正常,301代表跳转,404 not found也是状态码的一种,502以上代表服务器处理错误,诸如此类,想要进一步了解可以自行百度。

2.响应头。如内容类型、内容长度、服务器信息、设置Cookies等等。

3.响应体。最主要部分,包含了请求资源的内容,如网页HTML、图片二进制数据等。

爬虫获取的数据类型:网页文本:如HTML文档、Json格式文本等;图片:获取到的是二进制文件,保存为图片格式,写入时“wb”;视频:同为二进制文件,保存为视频格式即可;其他:只要是能请求到的,都能获取。

数据解析方式:
1.直接处理
2.Json解析
3.正则表达式
4.BeautifulSoup
5.PyQuery
6.XPath

保存数据:
1.文本。纯文本、Json、Xml等。
2.关系型数据库。如MySQL、Oracle、SQL Server等具有结构化表结构形式存储。
3.非关系型数据库。如MongoDB、Redis等Key-Value形式存储。
4.二进制文件。如图片、视频、音频等等直接保存成特定格式即可。

常用语法

import requests #导入requests库
url = "https://m.weibo.cn/"
response=requests.get(url) #发送请求
print(response.text) 
#获取源代码,可能会导致乱码建议使用response.content.decode(),
#注意此时获取的源代码是未经js渲染的源码,
#应该可以从审查元素>network>左栏选中m.weibo.cn>response中看到
print(response.headers) #获取请求头
print(response.status_code) #获取状态码

注:Network里的js(JavaScript)、css文件可以改变网页源码中的相应内容,称为渲染,渲染后的代码显示在审查元素Elements里。

那么如何解决渲染问题?

分析Ajax请求、Selenium/WebDriver、splash。 以webdriver例:

  from selenium import webdriver
  driver = webdriver.Chrome()
  driver.get("https://m.weibo.com")
  print(driver.page_source) #此时获取的代码与审查元素里的代码基本全一致

二、Urllib

什么是Urllib?

python内置的HTTP请求库

包含:

urllib.request 请求模块

urllib.error 异常处理模块

urllib.parse url解析模块

urllib.robotparser robots.txt解析模块等

详情请见 urllib官方文档

urllib.request:

Get类

import urllib.request
url = "http://www.baidu.com/"
response = urllib.request.urlopen(url)   #get类型,接收url地址
print(response.read())  #此时会发现打印内容为bytes类型,需要解码⬇️
print(response.read().decode("utf-8"))  #bytes类型,解码为网页源代码

Post类

import urllib.parse
import urllib.request
url_test = "http://httpbin.org/post" 
#国内访问httpbin不稳定,有可能存在访问不成功的情况。
#请尝试使用vpn,如若还是无法访问,可先行跳过,后面还会详细讲解post访问。
data = bytes(urllib.parse.urlencode({"word":"hello"}),encoding="utf-8") 
#data参数需要为bytes类型
response = urllib.request.urlopen(url_test,data=data) 
#加上data参数,即为post请求,不加默认get请求
print(response.read().decode("utf-8")) #打印出很多的关键信息

注:因为post类需要对其传入参数,因此将二进制的数据(字典类型)赋值给data,再由data对urlopen指定参数。成功访问后,最终输出中会出现所传入的字典数据。

Timeout

超时参数的设置

import socket
import urllib.error
import urllib.request
url_test = "http://www.baidu.com/"
try:
    response = urllib.request.urlopen(url_test, timeout=0.001)  
    #超时参数timeout,访问时间超过给定值,执行except语句。默认秒钟				
except urllib.error.URLError as e:
    if isinstance(e.reason,socket.timeout):  #判断出错原因为超时
       print("TIMEOUT")   #此举为了防止无期限地访问,因此设置超时时间

输出

TIMEOUT

Response

响应的一些基本属性

import urllib.request
url_test = "http://www.baidu.com"
response = urllib.request.urlopen(url_test)
print(type(response))  #response类型
print(response.status)  #访问状态码,成功访问一般为200
print(response.getheaders())  #响应头
print(response.getheader("Server"))  #获取响应头中特定的内容

urllib.request

import urllib.request
url_test = "http://www.baidu.com"
request = urllib.request.Request(url_test) #创造一个request的对象
response = urllib.request.urlopen(request)
print(response.read().decode("utf-8"))  
#这种创建对象的请求方式和之前效果相同,除此之外还可以向对象中添加额外信息

上面这种创建对象的请求方式和之前直接访问(不创建对象)效果相同,除此之外还可以向对象中添加额外信息:

import urllib.parse
import urllib.request
url = "http://httpbin.org/post"  #可能会需要vpn进行访问
headers = {"User-Agent":"Mozilla/4.0 (compatible;MSIE 5.5; Windows NT)",
		   "Host":"httpbin.org"}
dict = {"name":"Orange"}
data = bytes(urllib.parse.urlencode(dict),encoding="utf-8")
req = urllib.request.Request(url=url,data=data,headers=headers,method="POST")
#还可以用add方法添加headers
#req.add_header("Host","httpbin.org")
response = urllib.request.urlopen(req)
print(response.read().decode("utf-8"))

通过以上的方式可以对某一对象(例中为req)添加足够多的信息,再以此对象(req)为参数进行访问,简单明了。

Handler代理

需要特殊场合,可以切换IP,不易被网站识别为爬虫,但是由于我的资源有限,没办法演示出来…也许以后有机会单列一篇。

Cookie

存储在用户本地终端上的加密数据,是可以维持用户登录的信息

import urllib.request
import http.cookiejar
url = "http://www.baidu.com"
cookie = http.cookiejar.CookieJar() #生成一个cookie对象
handler = urllib.request.HTTPCookieProcessor(cookie) #模拟代理
opener = urllib.request.build_opener(handler)
response = opener.open(url) #response获得之后,之前声明的cookie被自动赋值
for item in cookie:
    print(item.name+"="+item.value)

输出

BAIDUID=F0364169BF4D6A3E69E0D56EE9D7895D:FG=1
BIDUPSID=F0364169BF4D6A3E69E0D56EE9D7895D
H_PS_PSSID=1455_21126_29523_29521_29098_29567_28837_29221_26350_29458_22159
PSTM=1565864013
delPer=0
BDSVRTM=0
BD_HOME=0

Cookie的文本保存:

import urllib.request
import http.cookiejar
url = "http://www.baidu.com"
filename = "cookie.txt"
cookie = http.cookiejar.MozillaCookieJar(filename) #会生成Mozilla格式的cookie
#cookie = http.cookiejar.LWPCookieJar(filename)  会生成LWP格式的cookie
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open(url)
cookie.save(ignore_discard=True,ignore_expires=True)

运行完成之后就可以在相应文件cookie.txt中看到对应cookie信息。

Cookie的文本读取:

import urllib.request
import http.cookiejar
url = "http://www.baidu.com"
cookie = http.cookiejar.LWPCookieJar()
cookie.load("cookie.txt",ignore_discard=True,ignore_expires=True)
#加载文本文件的Cookie
handler = urllib.request.HTTPCookieProcessor(cookie)
opener = urllib.request.build_opener(handler)
response = opener.open(url)
print(response.read().decode("utf-8"))

urllib.error

存在三种Error—URLError,HTTPError,ContentTooShortError,在urllib.error中会有各类的详细属性说明。

import urllib.request
import urllib.error
try:
    response = urllib.request.urlopen("http://caofan.com/index.htm")
    #尝试访问不存在的网页
except urllib.error.HTTPError as e: 
	#如果是HTTPError则执行此行,子类,此例中会执行该行代码,捕捉异常
    print(e.reason,e.code,e.headers,sep="\n")
except urllib.error.URLError as e: #如果是URLError则执行此行,父类
    print(e.reason)
else:
    print("Request Successfully")

输出

Not Found
404
Server: nginx/1.11.4
Date: Thu, 15 Aug 2019 10:17:06 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 169
Connection: close

通过异常还可以判断具体属性值:

import urllib.request
import urllib.error
import socket
try:
    response = urllib.request.urlopen("http://www.baidu.com",timeout=0.01)
except urllib.error.URLError as e:
    print(type(e.reason))
    if isinstance(e.reason,socket.timeout):
        print("TIMEOUT")

输出

<class 'socket.timeout'>
TIMEOUT

urllib.parse

**Urlparse:**主要作用是对url网址进行解析,分割等

import urllib.parse
#urllib.parse.urlparse(urlstring,scheme="",allow_fragments=True)
result=urllib.parse.urlparse("http://www.baidu.com/index.html;user?id=5#comment")
print(type(result),result,sep='\n')

输出如下:

<class 'urllib.parse.ParseResult'>
ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

若原网址中未指定协议类型,则传入scheme参数,也可在输出中看到相应协议:

import urllib.parse
result=urllib.parse.urlparse("www.baidu.com/index.html;user?id=5#comment",scheme="https")  
# 原网址中未指定协议类型,则传入scheme参数,也可在输出中看到相应协议
print(result)

输出如下:

ParseResult(scheme='https', netloc='', path='www.baidu.com/index.html', params='user', query='id=5', fragment='comment')

但若原网址中已有指定协议类型,再传入scheme参数,那么scheme参数不会生效:

import urllib.parse
result=urllib.parse.urlparse("http://www.baidu.com/index.html;user?id=5#comment",
							 scheme="https")
#尽管参数scheme被传递为“https”,但由于域名scheme已有,不会覆盖
print(result)

输出如下:

ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='user', query='id=5', fragment='comment')

将False传递给allow_fragments:

import urllib.parse
result=urllib.parse.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='')  #注意将False传递给allow_fragments后,fragment不再有内容,#comment向前填充

但若query中也为空,则会继续向前填充:

import urllib.parse
result=urllib.parse.urlparse("http://www.baidu.com/index.html;#comment",
                             allow_fragments=False)
print(result)

输出:

ParseResult(scheme='http', netloc='www.baidu.com', path='/index.html', params='#comment', query='', fragment='') # user?id=5缺失后,#comment继续向前填充

**urlunparse:**提供协议、域名、路径、参数进行网址拼接

data = ["http","www.baidu.com","index.html","user","a=6","comment"]
print(urllib.parse.urlunparse(data))

拼接结果如下

http://www.baidu.com/index.html;user?a=6#comment

Urljoin:
需要我们提供域名或者路径,具体拼接结果以后者域名为基准,后者域名没有的部分,由前者域名进行补充。

import urllib.parse
print(urllib.parse.urljoin("http://www.baidu.com","FAQ.html"))
#直接拼合
print(urllib.parse.urljoin("http://www.baidu.com","https://cuiqingcai.com/FAQ.html"))
#采用后者域名,若后者没有则用前面的url相应部分;若后者有则用后者的部分
print(urllib.parse.urljoin("http://www.baidu.com/about.html","https://cuiqingcai.com/FAQ.html"))
print(urllib.parse.urljoin("http://www.baidu.com/about.html","https://cuiqingcai.com/FAQ.html?question=2"))
print(urllib.parse.urljoin("http://www.baidu.com?wd=abc","https://cuiqingcai.com/index.php"))
print(urllib.parse.urljoin("http://www.baidu.com","?category=2#comment"))
print(urllib.parse.urljoin("www.baidu.com","?category=2#comment"))
print(urllib.parse.urljoin("www.baidu.com#comment","?category=2"))

输出如下:

http://www.baidu.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html
https://cuiqingcai.com/FAQ.html?question=2
https://cuiqingcai.com/index.php
http://www.baidu.com?category=2#comment
www.baidu.com?category=2#comment
www.baidu.com?category=2

Urlencode

把字典对象转换为get请求参数

params = {
   "name": "germey",
   "age": 22
}
base_url = "http://www.baidu.com?"
url = base_url + urllib.parse.urlencode(params) 
# urlencode方法能够将参数(字典)形式转化为& = 形式,可直接用于拼接url 
#另外,一般url参数会跟在  ? 后面
print(urllib.parse.urlencode(params))
print(url)

输出:

name=germey&age=22
http://www.baidu.com?name=germey&age=22

笔记中的大部分内容都来源于我看的视频教材,而我看的视频教材又以某自学吧为主,因此感兴趣的朋友可以私下联系我视频教材的具体链接。有些代码的输出内容涉及网页源码,由于内容过多,我就没有列出输出,还请谅解^_ 其实urllib库我使用的次数并不多,最多的还是requests库,我将在下一篇详细介绍,有什么问题建议欢迎提出哦!

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页