什么是爬虫
- 爬取网页数据的程序 它是一门工具
网页特征
- 每个网页都有不同的url(统一资源定位符)
- 网页都由HTML语言构成
- 都用HTTP/HTTPS协议传输
爬虫怎么爬取网页
- 定位你要的url地址
- 然后把网页地址下载下来
- 提取有用的数据,如果有 有用的URL,那继续爬
为什么用python做爬虫
python:代码简单易懂,并且第三方的库也有很多,python自带的urllib网络呢请求???requests网络请求模块,网络解析库xpath,BeautifulSoup4,pyquery等等,还有成熟
高效稳定的爬虫框架scrapy(pyspider)等等,并且还支持分布式爬虫(scrapy-redis)框架
java:是python写爬虫的最大的竞争对手,java的发展周期长,生态圈都比较完善,也有很多第三方库的支持,java的代码量比较大,开发的成本比较高,后期维护也比较繁琐
php:php曾经被叫做世界上最好的语言(一般用来后端的),也可以用来写爬虫,但是对多任务的支持不太好,爬虫对效率要求比较高,所有一般不适用php写爬虫
c/c++:比较偏向于底层的语言,代码的运行效率高,学习的门楷非常高,代码成型比较慢.
七层协议
应用层 表示层 会话程 传输层 网络层 数据链路层 物理层
OSI七层协议的目的:实现不同的系统互联之间的数据通讯,实现数据的传输.
应用层:http/https
传输层:TCP/UDP
TCP:网络传输协议,面向连接的,长连接,传输的是数据流
,确保数据的安全性和完整性,但是数据传输的效率低
UDP:网络传输协议,是非面向连接的,短连接,传输的是数据包,
传输数据是不安全的,可能会造成数据的丢失,传输速度非???/p>
http(超文本传输协议,端口号是80):
实现从网络传输草文本数据到本地浏览器的传送协议
https(端口号是443):是http的安全版本,在http的基础上添加了一个
SSL(安全套接字层)层,用于web端的安全传送,在传输层
对网络连接进行加密,
1.构建了一个安全的数据传输通道.
2.保证网站的真实性和有效性
https协议需要有一个证书(CA证书):由专门的证书机构颁发的,
也可以自己生成,但是访问的时候会提示连接不安全
https协议需要有一个证书(CA证书):由专门的证书机构颁发的,
也可以自己生成,但是访问的时候会提示连接不安全
http的工作原理:
URL介绍:
URI:统一资源标志符
URN:统一资源名称
URL:统一资源定位符
URI是URN和URL的父类
URL的组成部分:
https://baike.baidu.com/item/OSI/5520?fr=aladdin
https://book.qidian.com/info/1004608738
https://book.qidian.com/info/1004608738#Catalog
get:只是用于从服务器获取数据,再url连接后面
可能会跟一些查询参数
post:向服务器端提交数据,数据会放在请求体中,
一般用于添加或者修改数据
delete:用来删除数据
put:更新整个资源(用来做数据的更新)
patch:(更新资源)(局部数据的更新)
对比:get和post请求的区别
1.使用场景:get从服务器端获取数据,post请求向服务器端提交数据
2.安全性:get请求参数只拼接在url地址上,post请求会将参数放在
请求体中,(注意:不要误认为只要url地址后面添加了参数就是一个get请求)
3.get请求的url是有长度限制的,post的请求体中可以添加很多字段
Cookie和Session:目的保持会话
http请求是无状态的,每一次请求断开后,下一次请求就
认为是一个新的请求,为了维持请求状态就用到了Cookie
和Session
Cookie:保存在客户端的,记录信息确定用户的身份
Session:保存在服务端的,同样是记录信息确定用户身份
常见的请求状态码:
200:请求成功
3xx:重定向
301:永久重定向
302:临时重定向
4xx:客户端请求错误
400:请求错误,服务器无法解析
401:未授权,没有进行身份验证
403:服务器拒绝访问
404:访问的页面不存在
405:请求方式不允许
408:请求超时
5xx:服务端错误
500:服务端内部错误
501:服务器暂时不具备完成请求的功能
503:服务器不可用
urllib
request
#1.发起请求:python自带的urllib???#request:是urllib最基本的http网络请求???
from urllib import request
#https://www.qidian.com/all
#https://www.qidian.com/all
# ?orderId=&style=1&pageSize=20
# &siteid=1&pubflag=0&hiddenField=0&page=1
#https://www.qidian.com/all
#?orderId=&style=1&pageSize=20
# &siteid=1&pubflag=0&hiddenField=0&page=2
#https://www.qidian.com/all
# ?orderId=&style=1&pageSize=20
# &siteid=1&pubflag=0&hiddenField=0&page=3
#发起请求
"""
url:设置目标url
data=None:默认为None,表示发起的是一个get请求,
反之,不为None,表示发起的是一个post请求
timeout: 设置请求的超时时间(s)
cafile=None, 设置证书文件(一般不用)
capath=None, 设置证书文件路径(一般不用)
context=None, 一般设置为一个ssl的对象
(ssl._create_unverified_context()),
忽略未认证的CA证书(如果出现了ssl证书错误)
"""
url = 'https://www.qidian.com/all?orderId=&style=1&pageSize=20&siteid=1&pubflag=0&hiddenField=0&page=1'
#如果请求需要添加请求头,urlopen并没有headers参数来设置请求头
"""
url:设置目标url
data=None,:默认为None,表示发起的是一个get请求,
反之,不为None,表示发起的是一个post请求
headers={}:设置请求头,传递一个字段类型的参数
"""
req_header = {
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
#根据url构建一个请求对象
req = request.Request(url=url,headers=req_header)
#使用urlopen方法faqi请求,获得响应结果
response = request.urlopen(req,timeout=20)
# response = request.urlopen(url=url,timeout=20)
#从响应结果中获取相关数据
# print(response.read())
html = response.read().decode('utf-8')
print(len(html))
"""
文件读写模式:
r:打开一个文件,只有可读权限 rb: r+: rb+
w:打开一个文件,有写入的权限 wb: w+: wb+
a:打开一个文件,有追加权限 ab: a+: ab+:
"""
with open('quanshu.html','w') as file:
file.write(html)
#获取响应的状态码
code = response.status
print(code)
#获取响应的响应头
response_headers = response.getheaders()
print(response_headers)
#获取某一个响应头参数
server = response.getheader('Server')
print(server)
#获取当前请求的url地址
current_url = response.url
print(current_url)
#获取请求的reason(如果成功返回的是OK)
reason = response.reason
print(reason)
#注意:response.read(),只可以读取一次
#https://www.baidu.com/s?wd=新年愿望
error
# error模块:在我们请求过程中,可能因为服务器错误,弱网环境...
# 造成请求失败,这时我们需要对这些错误进行异常处理,不然会造成代码崩溃
from urllib import request,error
'''
error.URLError:
产生的原因主要有:
没有网络连接
服务器连接失败
找不到指定的服务器
有一个reason属性:返回错误的原因
'''
'''
error.HTTPError
HTTP请求错误,比如未认证,页面不存在
code:请求的状态码
reason:返回错误的原因
headers:返回响应头部
'''
import ssl
context = ssl._create_unverified_context()
# url = 'https://www.baidu.com/12345.htm'
url = 'https://www.qidian.com/all/nsacnscn.htm'
try:
response = request.urlopen(url,timeout=0.01,context=context)
print(response.status)
except error.HTTPError as err:
print('HTTPError')
print(err.code)
print(err.reason)
print(err.headers)
except error.URLError as err:
print('URLError',err.reason)
parse
#parse:可以对url进行拆分,组合,编码,解码,拼接
from urllib import parse
#parse.urlencode():将字典类型的参数转为url编码格式
form_data ={
'first': 'false',
'pn': 2,
'kd': '后端',
}
#get请求直接使用urlencode将参数转为url编码格式
form_data1 = parse.urlencode(form_data)
print(form_data1)
#post请求urlencode将参数转为url编码格式,然后使
# 用encode方法将字符串转为bytes类型
form_data2 = parse.urlencode(form_data).encode('utf-8')
print(form_data2)
# parse.parse_qs():将url编码格式的字符串转化为字典类型
#注意key对应的value是一个list
parmas = parse.parse_qs(form_data1)
print(parmas)
#parse.quote()将中文字符转为url编码的字符
key = '我的国'
result = parse.quote(key)
print(result)
#parse.unquote()将url编码的字符转换为中文字符
unquote_result = parse.unquote(result)
print(unquote_result)
#将不完整的url参照基类url,拼接完整
base_url = 'http://www.qidian.com/book/123456.html'
sub_url = '12345789.html'
full_url = parse.urljoin(base_url,sub_url)
print(full_url)
#parse.urlparse():将url进行拆分
base_url = 'http://www.qidian.com/book/123456.html'
result = parse.urlparse(base_url)
"""
ParseResult(
scheme='http',:协议
netloc='www.qidian.com', ip或域
path='/book/123456.html', 路径
params='', 参数
query='', 查询参数(?后面拼接的参数)
fragment='' : 锚点
)
"""
print(result)
print(result.scheme)
#parse.urlunparse():将url的各个部分合并为一个完整的url
#scheme, netloc, url, params, query, fragment
url_datas = ('https','www.baidu.com','book','','wd=xxx','1234')
full_url = parse.urlunparse(url_datas)
print(full_url)
正则表达式
#正则的规则:
#单字符匹配
"""
. 除换行符之外的任意字符
\d 表示数字
\D 匹配非数字
\w 匹配单词字符[a-z,A-Z,0-9]
\W 匹配非单词字符
\s 匹配空白字符,空格,\n \t ...
\S 匹配非空白字符
^ 匹配以...开头
$ 匹配以...结尾
[0-9] => \d 匹配0-9
"""
#多字符匹配(贪婪匹配)
"""
* 匹配*前面的字符任意次数
+ 匹配+前面的字符至少1次
? 匹配?前面的字符0~1次
{n,m} 匹配{n,m}前面的字符n~m次
"""
#多字符匹配(非贪婪匹配)
"""
*?
+?
??
"""
#其他
"""
() 分组
| 逻辑或
\ 转义字符
"""
#re??橄碌姆椒?
import re
#re.compile():构建正则表达式对象
#re.match():从起始位置开始匹配,单次匹配,匹配到
#结果立即返回,反之,返回None
str = 'abcdebfga'
pattern = re.compile('b|e')
result = re.match(pattern,str)
if result:
print(result.group())
# re.search():在整个字符串中进行匹配,单次匹配,匹配到结果
# 立即返回,反之,返回None
result = re.search(pattern,str)
print(result.group())
# re.findall():匹配出整个字符串中,所有符合正则规则的结果
# 返回的是一个列表(list)
result = re.findall(pattern,str)
print(result)
# re.finditer():匹配出整个字符串中所有符合正则规则的结果,
# 返回的是一个可迭代对象
result = re.finditer(pattern,str)
print(type(result),result)
for i in result:
print(i,type(i))
print(i.group())
# re.sub():根据正则表达式进行字符串替换
new_str = re.sub(pattern,'h',str)
print(new_str)
#re.split():根据正则表达式进行分割,得到的是一个list
result = re.split(pattern,str)
print(result)