Pytthon爬虫-基础
Intro
- 网络爬虫是什么:
- 网络爬虫是一个模仿人类请求网站行为的程序。可以自动请求网页、以及抓取相关数据
- 为何用Python写爬虫程序
- python语法优美,代码简洁,开发效率高,相关的HTTP请求模块和HTML解析模块非常丰富。还有scrapy和scrapy-redis框架让我们开发爬虫变得异常容易
- 爬虫的实际例子
-
- 搜索引擎
- 2.伯乐在线
-
- 惠惠购物助手
-
- 数据分析与研究(数据冰山知乎专栏)
- 5.抢票软件等
-
- 通用爬虫和聚焦爬虫
- 通用爬虫是搜索引擎抓取系统的重要组成部分,主要是将互联网上的网页下载到本地,形成一个互联网内容的镜像备份
- 聚焦爬虫是面向特定需求的一种网络爬虫程序,在实施网页抓取的时候会对内容进行筛选和处理,尽量保证只抓取与需求相关的网页信息
http协议和chrome抓包工具
-
http/https协议
- HTTP协议:超文本传输协议,一种传输.html文件的规范。服务器端口默认为80
- HTTPS协议:是HTTP协议的加密版本,在HTTP下加入了SSL层,服务器端口默认是443
-
在浏览器发送一个http请求的过程
-
- 在浏览器地址栏输入一个URL并按下回车,浏览器会向对应URL所在的HTTP服务器发送HTTP请求(主要分为GET和POST两种请求方式)
-
- 比如输入的URL是https://baidu.com,浏览器会发送一个Request请求试图获取百度首页的html文件,百度的服务器接收到这个请求后使用Request所在的Socket向本地的浏览器发送回Response文件对象
-
- 浏览器分析Response中的HTML,发现其中引用的很多其他文件,比如image.CSS.JS文件,浏览器会自动再次发送Request取获取相应的图片,样式等
-
- 当所有的文件都下载成功后,网页会根据HTML语法结构完整地在浏览器界面显示出来
-
-
url详解
- Uniform Resource Locator 统一资源定位符。一个URL有以下几部分组成:
scheme://host:port/path/?query-string=xxx#anchor
- scheme:代表访问协议,一般为http/https以及ftp等
- host:主机名,域名,比如localhost,www.baidu.com
- port:端口号:当你访问一个网站时,浏览器默认使用80端口
- path:查找路径。比如:
www.tencent.com/game/LOL
,后面的game/LOL
就是path - query-string:查询字符串,比如
www.baidu.com/s?wd=python
问号,后面的wd=python
就是查询字符串 - anchor:锚点,前端用来做页面定位的标识符,类似于markdown语法中的标题
- 在浏览器中请求一个url,浏览器会对这个url进行一个编码。除英文字母,数字和部分符号外,其他的都用
百分号+十六进制码值
进行编码
- Uniform Resource Locator 统一资源定位符。一个URL有以下几部分组成:
-
常用请求方法
- 在http协议中,定义了八种请求方法,其中最常用的两种分别是
GET
和POST
请求get
请求:一般情况下,只从服务器获取数据下来,并不会对服务器资源产生任何影响的时候会使用get
请求post
请求:向服务器发送数据(登陆)、上传文件等,会对服务器资源产生影响的时候会使用post
请求。- 最直观的区别:
- get请求的请求参数是作为一个key/value对的序列(查询字符串)附加到URL上的。
- post的请求参数是在http标题的一个不同部分(名为entity body)传输的,这一部分用来传输表单信息,因此必须将Content-type设置为:application/x-www-form-urlencoded。
- 行为上的区别
- GET请求会被浏览器主动cache,而POST不会,除非手动设置。
- GET产生一个TCP数据包;POST产生两个TCP数据包,GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
- 在http协议中,定义了八种请求方法,其中最常用的两种分别是
-
请求头常见参数
-
在http协议中,向服务器发送一个请求,数据分为三部分。
- 第一个是把数据放在url中
- 第二个是把数据放在body中(post请求)
- 第三个是把数据放在head中
-
在网络爬虫中经常会用到的一些请求头参数
- User-Agent:浏览器名称。这个在网络爬虫中经常会被使用到。请求一个网页的时候,服务器通过这个参数就可以知道这个请求是由哪种浏览器发送的。如果我们是通过爬虫发送请求,那么我们的User-Agent就是Python,这对于那些有反爬虫机制的网站来说,可以轻易的判断你这个请求是爬虫。因此我们要经常设置这个值为一些浏览器的值,来伪装我们的爬虫。
- Referer:表明当前这个请求是从哪个url过来的。这个一般也可以用来做反爬虫技术。如果不是从指定页面过来的,那么就不做相关的响应。
- Cookie:http请求是无状态的。也就是说即使第一次和服务器连接后并且登录成功后,第二次请求服务器依然不能知道当前请求是哪个用户。
- cookie的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie只能存储一些小量的数据。
- cookie的格式:
- Set-Cookie: NAME=VALUE;Expires/Max-age=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
- 参数意义:
- NAME:cookie的名字
- VALUE:cookie的值。
- Expires:cookie的过期时间。
- Path:cookie作用的路径。
- Domain:cookie作用的域名。
- SECURE:是否只在https协议下起作用。
- 参数意义:
- Set-Cookie: NAME=VALUE;Expires/Max-age=DATE;Path=PATH;Domain=DOMAIN_NAME;SECURE
-
-
常见响应状态码:
- 200:请求正常,服务器正常的返回数据。
- 301:永久重定向。比如在访问www.jingdong.com的时候会重定向到www.jd.com。
- 302:临时重定向。比如在访问一个需要登录的页面的时候,而此时没有登录,那么就会重定向到登录页面。
- 400:请求的url在服务器上找不到。换句话说就是请求url错误。
- 403:服务器拒绝访问,权限不够。
- 500:服务器内部错误。可能是服务器出现bug了。
-
Chrome抓包工具:
- Chrome浏览器是一个非常亲近开发者的浏览器。可以方便的查看网络请求以及发送的参数。对着网页右键->检查。然后就可以打开开发者选项。
- 快捷键F12
- 开发者选项中导航栏
- Elements
- 构成这个网页的源代码
- console
- 这个网页的控制台
- Sources
- 组成这个网页的所有源文件
- Network
- 加载这个页面的时候,浏览器发送的所有请求
- Elements
- Chrome浏览器是一个非常亲近开发者的浏览器。可以方便的查看网络请求以及发送的参数。对着网页右键->检查。然后就可以打开开发者选项。
urlib库
-
urllib.request模块:在Python3的urllib库中,所有和网络请求相关的方法,都被集到下面了
- urlopen函数
-
es:访问百度获取相关资源
from urllib import request resp = request.urlopen('http://www.baidu.com') print(resp.read())
- 参数
- url:请求的url。
- data:请求的data,如果设置了这个值,那么将变成post请求。
- 返回值:返回值是一个http.client.HTTPResponse对象,这个对象是一个类文件句柄对象。有read(size)、readline、readlines以及getcode等方法。
- 参数
-
- urlretrieve函数
-
这个函数可以方便的将网页上的一个文件保存到本地。
es:将百度的首页下载到本地:from urllib import request request.urlretrieve('http://www.baidu.com/','baidu.html')
- 参数
- url:请求路径
- filepath:保存的文件路径
- 参数
-
- ProxyHandler处理器(代理设置)
-
很多网站会检测某一段时间某个IP的访问次数(通过流量统计,系统日志等),如果访问次数多的不像正常人,它会禁止这个IP的访问。
所以我们可以设置一些代理服务器,每隔一段时间换一个代理,就算IP被禁止,依然可以换个IP继续爬取。-
urllib中通过ProxyHandler来设置使用代理服务器,下面代码说明如何使用自定义opener来使用代理:
from urllib import request # 这个是没有使用代理的 resp = request.urlopen('http://httpbin.org/get') print(resp.read().decode("utf-8")) # 这个是使用了代理的 handler = request.ProxyHandler({"http":"218.66.161.88:31769"}) opener = request.build_opener(handler) req = request.Request("http://httpbin.org/ip") resp = opener.open(req) print(resp.read())
-
常用的代理
-
-
- urlopen函数
-
urllib.parse模块:所有涉及编码译码的方法,以及各种解析操作
-
urlencode函数
- 如果使用代码发送请求,对于url中包含了中文或者其他特殊字符的情况,必须手动的进行编码,这时候就应该使用urlencode函数来实现。urlencode可以把字典数据转换为URL编码的数据。
- es:
from urllib import parse data = {'name':'爬虫基础','greet':'hello world','age':100} qs = parse.urlencode(data) print(qs)
- es:
- 如果使用代码发送请求,对于url中包含了中文或者其他特殊字符的情况,必须手动的进行编码,这时候就应该使用urlencode函数来实现。urlencode可以把字典数据转换为URL编码的数据。
-
parse_qs函数
- 可以将经过编码后的url参数进行解码。
- es:
from urllib import parse qs = "name=%E7%88%AC%E8%99%AB%E5%9F%BA%E7%A1%80&greet=hello+world&age=100" print(parse.parse_qs(qs))
- es:
- 可以将经过编码后的url参数进行解码。
-
urlparse和urlsplit
- 有时候拿到一个url,想要对这个url中的各个组成部分进行分割,那么这时候就可以使用urlparse或者是urlsplit来进行分割。
-
es:
from urllib import request,parse url = 'http://www.baidu.com/s?username=zhiliao' result = parse.urlsplit(url) result = parse.urlparse(url) print('scheme:',result.scheme) print('netloc:',result.netloc) print('path:',result.path) print('query:',result.query)
-
注意:urlparse和urlsplit基本上是一模一样的。唯一不一样的地方是,urlparse里面多了一个params属性,而urlsplit没有这个params属性。
- 比如有一个url为:
url = 'http://www.baidu.com/s;hello?wd=python&username=abc#1'
,
那么urlparse可以获取到hello,而urlsplit不可以获取到。url中的params也用得比较少
- 比如有一个url为:
-
- 有时候拿到一个url,想要对这个url中的各个组成部分进行分割,那么这时候就可以使用urlparse或者是urlsplit来进行分割。
-
-
request.Request类:自定义请求头
- 如果想要在请求的时候增加一些请求头,那么就必须使用request.Request类来实现。
- 比如要增加一个User-Agent,示例代码如下:
from urllib import request headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36' } req = request.Request("http://www.baidu.com/",headers=headers) resp = request.urlopen(req) print(resp.read())
- 比如要增加一个User-Agent,示例代码如下:
- 如果想要在请求的时候增加一些请求头,那么就必须使用request.Request类来实现。
requests库
-
虽然Python的标准库中 urllib模块已经包含了平常我们使用的大多数功能,但是它的 API 使用起来让人感觉不太好,而 Requests宣传是 “HTTP for Humans”,说明使用更简洁方便。
-
发送GET请求:
-
最简单的发送get请求就是通过requests.get来调用:
-
response = requests.get("http://www.baidu.com/")
-
-
添加headers和查询参数:
- 如果想添加 headers,可以传入headers参数来增加请求头中的headers信息。
- 如果要将参数放在url中传递,可以利用 params 参数。
-
import requests kw = {'wd':'中国'} headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36"} # params 接收一个字典或者字符串的查询参数,字典类型自动转换为url编码,不需要urlencode() response = requests.get("http://www.baidu.com/s", params = kw, headers = headers) # 查看响应内容,response.text 返回的是Unicode格式的数据 print(response.text) # 查看响应内容,response.content返回的字节流数据 print(response.content) # 查看完整url地址 print(response.url) # 查看响应头部字符编码 print(response.encoding) # 查看响应码 print(response.status_code)
-
-
发送POST请求:
-
最基本的POST请求可以使用post方法:
-
response = requests.post("http://www.baidu.com/",data=data)
-
-
传入data数据:
这时候就不要再使用urlencode进行编码了,直接传入一个字典进去就可以了。-
import requests url = "https://www.lagou.com/jobs/positionAjax.json?city=%E6%B7%B1%E5%9C%B3&needAddtionalResult=false&isSchoolJob=0" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', 'Referer': 'https://www.lagou.com/jobs/list_python?labelWords=&fromSearch=true&suginput=' } data = { 'first': 'true', 'pn': 1, 'kd': 'python' } resp = requests.post(url,headers=headers,data=data) # 如果是json数据,直接可以调用json方法 print(resp.json())
-
-
-
使用代理:
- 使用requests添加代理也非常简单,只要在请求的方法中(比如get或者post)传递proxies参数就可以了。
-
import requests url = "http://httpbin.org/get" headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36', } proxy = { 'http': '171.14.209.180:27829' } resp = requests.get(url,headers=headers,proxies=proxy) with open('xx.html','w',encoding='utf-8') as fp: fp.write(resp.text)
-
- 使用requests添加代理也非常简单,只要在请求的方法中(比如get或者post)传递proxies参数就可以了。
-
cookie:
- 如果在一个响应中包含了cookie,那么可以利用cookies属性拿到这个返回的cookie值
-
import requests url = "http://www.renren.com/PLogin.do" data = {"email":"970138074@qq.com",'password':"pythonspider"} resp = requests.get('http://www.baidu.com/') print(resp.cookies) print(resp.cookies.get_dict())
-
- 如果在一个响应中包含了cookie,那么可以利用cookies属性拿到这个返回的cookie值
-
session:
- 在使用urllib库时,是可以使用opener发送多个请求,多个请求之间是可以共享cookie的。如果使用requests,也要达到共享cookie的目的,那么可以使用requests库给我们提供的session对象。注意,这里的session不是web开发中的那个session,这个地方只是一个会话的对象而已。还是以登录人人网为例,使用requests来实现。
-
import requests url = "http://www.renren.com/PLogin.do" data = {"email":"970138074@qq.com",'password':"pythonspider"} headers = { 'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36" } #登录 session = requests.session() session.post(url,data=data,headers=headers) #访问大鹏个人中心 resp = session.get('http://www.renren.com/880151247/profile') print(resp.text)
-
- 在使用urllib库时,是可以使用opener发送多个请求,多个请求之间是可以共享cookie的。如果使用requests,也要达到共享cookie的目的,那么可以使用requests库给我们提供的session对象。注意,这里的session不是web开发中的那个session,这个地方只是一个会话的对象而已。还是以登录人人网为例,使用requests来实现。
-
处理不信任的SSL证书:
-
对于那些已经被信任的SSL证书的网站,比如https://www.baidu.com/,那么使用requests直接就可以正常的返回响应。
-
而对于没被信任的SSL证书的网站,可以在request.get中传递参数
verify=False
,手动设置不再验证该网站的SSL证书resp = requests.get('http://www.12306.cn/mormhweb/',verify=False) # 跳过验证SSL证书即可访问不被信任的网站 print(resp.content.decode('utf-8'))
-