前置知识
什么是互联网爬虫?
如果我们把互联网比作一张大的蜘蛛网,那一台计算机上的数据便是蜘蛛网上的一个猎物,而爬虫程序就是一只小蜘蛛,沿着蜘蛛网抓取自己想要的数据

解释1: 通过一个程序,根据url(http://www.taobao.com)进行爬取网页,获取有用信息
解释2:使用程序模拟浏览器,去向服务器发送请求,获取响应信息
爬虫核心?
- 爬取网页: 爬取整个网页 包含了网页中所有得内容。
- 解析数据:将网页中你得到的数据进行解析
- 难点:爬虫和反爬虫之间的博弈
爬虫的用途?

- 数据分析/人工数据集
- 社交软件冷启动
- 舆情监控
- 竞争对手监控。
爬虫分类?
-
通用爬虫
实例
百度、360、google、sougou等搜索引擎---伯乐在线
功能
访问网页->抓取数据->数据存储->数据处理->提供检索服务
robots协议
一个约定俗成的协议,添加robots.txt文件,来说明本网站哪些内容不可以被抓取,起不到限制作用自己写的爬虫无需遵守。
网站排名(SEO)
1.根据pagerank算法值进行排名(参考个网站流量、点击率等指标)
2.百度竞价排名
缺点
1.抓取的数据大多是无用的
2.不能根据用户的需求来精准获取数据 -
聚焦爬虫
功能
根据需求,实现爬虫程序,抓取需要的数据
设计思路
1.确定要爬取的url
如何获取url
2.模拟浏览器通过http协议访问url,获取服务器返回的html代码
如何访问
3.解析html字符串(根据一定规则提取需要的数据)
如何解析
反爬手段?
1.User-Agent
User Agent中文名为用户代理,简称UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
2.代理IP
西次代理
快代理
什么是高匿名、匿名和透明代理?它们有什么区别?
1.使用透明代理,对方服务器可以知道你使用了代理,并且也知道你的真实IP。
2.使用匿名代理,对方服务器可以知道你使用了代理,但不知道你的真实IP。
3.使用高匿名代理,对方服务器不知道你使用了代理,更不知道你的真实IP。
3.验证码访问
打码平台
云打码平台
超级🦅
4.动态加载网页 网站返回的是js数据并不是网页的真实数据
selenium驱动真实的浏览器发送请求
5.数据加密
分析js代码
urllib库使用
urllib使python自带的库, 主要学习以下6个方法
urllib.request.urlopen() 模拟浏览器向服务器发送请求
response 服务器返回的数据
response的数据类型是HttpResponse
字节-->字符串
解码decode
字符串-->字节
编码encode
read() 字节形式读取二进制 扩展:rede(5)返回前几个字节
readline() 读取一行
readlines() 一行一行读取 直至结束
getcode() 获取状态码
geturl() 获取url
getheaders() 获取headers
urllib.request.urlretrieve()
请求网页
请求图片
请求视频
urllib的基本使用
import urllib.request
url = 'http://www.baidu.com'
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(url)
# 解析响应的数据
content = response.read().decode('utf-8')
# 打印数据
print(content)

import urllib.request
url = 'http://www.baidu.com'
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(url)
# 一个类型和6个方法
# 类型: response是一个HTTPResponse对象
print(type(response))
# 方法1: 一个字节一个字节的读取
# content = response.read()
# 方法2: 指定读取多少个字节
# content = response.read(20)
# 方法3: 读取一行
# content = response.readline()
# 方法4: 读取所有行
content = response.readlines()
# 方法5: 获取响应状态码
code = response.getcode()
# 方法6: 获取响应头信息
headers = response.getheaders()
下载
import urllib.request
# 下载网页
# url_page = 'http://www.baidu.com'
# urllib.request.urlretrieve(url_page, 'page.html')
# 下载图片
# url_img = 'https://img0.baidu.com/it/u=647326367,1585486728&fm=253&fmt=auto&app=120&f=JPEG?w=500&h=653'
# urllib.request.urlretrieve(url=url_img, filename= 'img3.jpg')
# 下载视频
url_video = 'https://vd9.bdstatic.com/mda-rhpq7j4xwp3ikjni/mb/cae_h264/1756055205656822141/mda-rhpq7j4xwp3ikjni.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1775598185-0-0-00c30dbe924b0d8c6cf688745691469d&bcevod_channel=searchbox_feed&pd=1&cr=0&cd=0&pt=3&logid=2585762991&vid=17348459069101845158&klogid=2585762991&abtest='
urllib.request.urlretrieve(url_video, 'video.mp4')

视频下载地址的获取

请求对象的定制
- UA介绍 :User Agent中文名为用户代理,简称UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、cPU 类型、浏览器及版本。浏览器内核、浏览器渲染引擎、浏览器语言、浏览器插件等

- 直接请求看一下
-
只能回来一部分数据, 因为http有ssl安全协议, 直接请求的时候缺少UA参数, 服务器识别为非浏览器请求
-
这就是我们认识的第一种反爬手段, UA反爬
import urllib.request
url = 'https://www.baidu.com'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}response = urllib.request.urlopen(url)
content = response.read().decode('utf-8')
print(content)

-
通过语法: request = urllib.request.Request() 设置UA数据
import urllib.request
url = 'https://www.baidu.com'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}完整的参数列表是 url,data,headers, ...
形参和实参位置不对应的时候要使用关键字传参, 就是headers=headers这种写法
request = urllib.request.Request(url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)

编解码
-
单个参数编码 urllib.parse.quote()
import urllib.request
import urllib.parse1. 手动编码拼接参数
直接请求会报错, 'ascii' codec can't encode characters in position 10-12: ordinal not in range(128)
url = 'https://www.baidu.com/s?wd=周杰伦'
因为"周杰伦"是中文, 不在ascii编码范围内
编码会的请求是这样子
https://www.baidu.com/s?wd=周杰伦
base_url = 'https://www.baidu.com/s?wd='
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}使用 urllib.parse.quote() 进行unicode编码
new_url = base_url + urllib.parse.quote("周杰伦")
print("new_url==", new_url)request = urllib.request.Request(url=new_url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content) -
复杂参数编码 urllib.parse.urlencode()
import urllib.request
import urllib.parse2. 自动编码拼接参数, 适合多参数/
base_url = 'https://www.baidu.com/s?'
data = {
"wd": "周杰伦",
"sex": "男",
"location": "中国"
}
data_code = urllib.parse.urlencode(data)
new_url = base_url + data_code
print("new_url==", new_url)headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}设置请求对象
request = urllib.request.Request(url=new_url, headers=headers)
模拟请求
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content) -
post请求的参数处理 urllib.parse.urlencode().encode('utf-8')
import urllib.request
import urllib.parse
import json3.post请求的参数必须进行编码
base_url = 'https://fanyi.baidu.com/sug'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}data = {
"kw": "spider"
}post请求的参数必须进行编码
data_code = urllib.parse.urlencode(data).encode('utf-8')
创建请求对象
request = urllib.request.Request(url=base_url, data=data_code, headers=headers)
模拟请求
response = urllib.request.urlopen(request)
解析响应数据
content = response.read().decode('utf-8')
print(type(content)) # <class 'str'> 类型
把json字符串转换成字典, 方便查看
obj = json.loads(content)
print(obj)

- 总结:post和get区别?
- get请求方式的参数必须编码,参数是拼接到url后面,编码之后不需要调用encode方法
- post请求方式的参数必须编码,参数是放在请求对象定制的方法中,编码之后需要调用encode方法
认识cookie
百度详细翻译案例
import urllib.request
import urllib.parse
url = 'https://fanyi.baidu.com/v2transapi'
headers = {
'Cookie': 'REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1;'
'SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; PSTM=1537097513;BIDUPSID=D96F9A49A8630C54630DD60CE082A55C;'
'BAIDUID=0814C35D13AE23F5EAFA8E0B24D9B436:FG=1;to_lang_often=%5B%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%2C%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%5D;'
'from_lang_often=%5B%7B%22value%22%3A%22zh%22%2C%22text%22%3A%22%u4E2D%u6587%22%7D%2C%7B%22value%22%3A%22en%22%2C%22text%22%3A%22%u82F1%u8BED%22%7D%5D; BDORZ=B490B5EBF6F3CD402E515D22BCDA1598;delPer=0; H_PS_PSSID=1424_21115_29522_29519_29099_29568_28835_29220_26350; PSINO=2;'
'ocale=zhHm_lvt_64ecd82404c51e03dc91cb9e8c025574=1563000604,1563334706,1565592510Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1565592510yjs_js_security_passport=2379b52646498f3b5d216e6b21c6f1c7bf00f062_1565592544_js'
}
data = {
'from': 'en',
'to': 'zh',
'query': 'you',
'transtype': 'realtime',
'simple_means_flag': '3',
'sign': '269482.65435',
'token': '2e0f1cb44414248f3a2b49fbad28bbd5',
}
# 参数的编码
data = urllib.parse.urlencode(data).encode('utf‐8')
# 请求对象的定制
request = urllib.request.Request(url=url, headers=headers, data=data)
response = urllib.request.urlopen(request)
# 请求之后返回的所有的数据
content = response.read().decode('utf‐8')
# loads将字符串转换为python对象
obj = json.loads(content)
# python对象转换为json字符串 ensure_ascii=False 忽略字符集编码
s = json.dumps(obj, ensure_ascii=False)
print(s)

ajax的get请求
案例: 豆瓣电影
# 爬取豆瓣电影前10页数据
# https://movie.douban.com/j/chart/top_list?type=20&interval_id=100%3A90&action=&start=0&limit=20
# https://movie.douban.com/j/chart/top_list?type=20&interval_id=100%3A90&action=&start=20&limit=20
# https://movie.douban.com/j/chart/top_list?type=20&interval_id=100%3A90&action=&start=40&limit=20
# 找规律
# page 1 2 3 4
# start 0 20 40 60
# start = (page - 1) * 20
import urllib.request
import urllib.parse
def create_request(page):
base_url = 'https://movie.douban.com/j/chart/top_list?type=20&interval_id=100%3A90&action=&'
# 创建参数
data = {
'start': (page - 1) * 20,
'limit': 20
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}
# 对参数进行编码
data = urllib.parse.urlencode(data)
# 拼接url
url = base_url + data
# 创建请求对象
request = urllib.request.Request(url=url, headers=headers)
return request
def get_content(request):
response = urllib.request.urlopen(request)
content = response.read().decode('utf‐8')
return content
def down_load(page, content):
# with open(文件的名字,模式,编码) as fp:
# fp.write(内容)
with open('data/douban_' + str(page) + '.json', 'w', encoding='utf‐8') as fp:
fp.write(content)
if __name__ == '__main__':
for page in range(1, 11):
# 创建请求对象
request = create_request(page)
# 请求数据
content = get_content(request)
# 保存数据
down_load(page, content)

ajax的post请求
案例: KFC
# 1页
# http://www.kfc.com.cn/kfccda/ashx/GetStorelist.ashx?op=cname
# #post
# cname:北京
# pid:
# pageIndex: 1
# pagesize:10
# 2页
# http://www.kfc.com.cn/kfccda/ashx/GetStorelist.ashx?op=cname
# #post
# cname:北京
# pid:
# pageIndex: 2
# pagesize:10
import urllib.parse
import urllib.request
def create_request(page):
base_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36"
}
data = {
'cname': '北京',
'pid': '',
'pageIndex': page,
'pagesize': 10
}
data = urllib.parse.urlencode(data).encode('utf-8')
request = urllib.request.Request(url=base_url, data=data, headers=headers)
return request
def get_content(request):
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
return content
def down_load(page, content):
with open('data/kfc_' + str(page) + '.json', 'w', encoding='utf-8') as fp:
fp.write(content)
if __name__ == '__main__':
for page in range(1, 11):
# 创建请求对象
request = create_request(page)
# 模拟请求
content = get_content(request)
# 保存数据
down_load(page, content)
URLError\HTTPError
-
HTTPError类是URLError类的子类
-
导入的包urllib.error.HTTPError 或 urllib.error.URLError
-
http错误: http错误是针对浏览器无法连接到服务器而增加出来的错误提示。引导并告诉浏览者该页是哪里出了问题。
-
通过urllib发送请求的时候,有可能会发送失败,这个时候如果想让你的代码更加的健壮,可以通过try except进行捕获异常,异常有两类,URLError/HTTPError
import urllib.request
import urllib.errorurl = 'https://mp.csdn.net/mp_blog/creation/editor/159887412'
url = 'http://www.goudan11111.com
headers = {
'cookie': 'UN=CSDN20221005; uuid_tt_dd=163602-351516; fid=20_96735975599-1762-486861; c_dls://so.csdn.net/so/search; _ga=GA1.2.260231072.1760613870; _ga_7W1N0GEY1P=GS2.1.s1765542085o43g0t1765542085j60l0h0; UserName=CSDN20221005; UserInfo=0ce910cfa1124ec8bde573f352e0650c; UserToken=0ce910cfa1124ec8bde573f352e0650c; UserNick=%E5%BF%AB%E7%A0%81%E8%BD%AF%E4%BB%B6; AU=B9E; BT=1766363478052; p_uid=U010000; c_dl_prid=1769936316588_544489; c_dl_rid=1769936342660_385249; c_dl_fpage=/download/ashyyyy/91030805; c_dl_um=distribute.pc_search_result.none-task-c_download-2%7Eall%7EElasticCommercialInsert%7Esearch_v2-2-1anzdviqqk-null-null.142%5Ev102%5Epc_search_result_base6; c_ins_prid=-; c_ins_rid=1770014611135_541230; c_ins_fref=https://www.csdn.net/; c_ins_fpage=/index.html; c_ins_um=-; ins_first_time=1770014610934; csdn_newcert_CSDN20221005=1; c_ab_test=1; FCCDCF=%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2C%5B%5B32%2C%22%5B%5C%2221681bce-275e-4845-9978-bbc037cc981e%5C%22%2C%5B1761633171%2C239000000%5D%5D%22%5D%5D%5D; FCNEC=%5B%5B%22AKsRol_pP_GXVQvYpjLEplY9AeV5ENQLPA6J-xUDcveQt4VsEq-KJy5Ml6ls7lCULJC-vs6rJ8r-9HrwpuMcXkMZWyRkUJwxICFnNXXeaspGhZMA5Atr7XaDzi094OjB7DEfaMFIfthpyWuMkEIedvzDbakO_gojCA%3D%3D%22%5D%5D; __gads=ID=ea6a7d6b7d15ab06:T=1748843494:RT=1775460248:S=ALNI_MZPSJmIwp9W7mTxU89zLwbiOsBtAA; __gpi=UID=0000110f654eeef3:T=1748843494:RT=1775460248:S=ALNI_MbN5ctn7OBCsYjBuTy9ltxmFCt3vw; __eoi=ID=d177a950eb87817e:T=1769927668:RT=1775460248:S=AA-AfjbOinB7IF6miwt2xOFp5ZhW; creative_btn_mp=3; dc_sid=cf4dd0485fe07aa408bbf4f99e84d82c; dc_session_id=10_1775723383757.429230; c_first_ref=default; c_segment=12; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1775586013,1775641301,1775685686,1775723384; HMACCOUNT=E1ECC4AAB5140464; c-sidebar-collapse=0; _clck=1j2b4a1%5E2%5Eg52%5E0%5E1524; _clsk=1r5hgju%5E1775723419348%5E2%5E0%5En.clarity.ms%2Fcollect; c_pref=default; c_first_page=https%3A//blog.csdn.net/ityard/article/details/102646738; c_dsid=11_1775723496099.453642; c_page_id=default; c_ref=https%3A//blog.csdn.net/csdnnews/article/details/159979326%3Fspm%3D1000.2115.3001.5927; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1775723513; log_Id_view=134; log_Id_click=5; log_Id_pv=14; dc_tos=td7x1e',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36'
}try:
request = urllib.request.Request(url=url, headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf‐8')
print(content)
except urllib.error.HTTPError as e:
# 请求已经成功到达服务器,但服务器返回了错误响应
print('HTTPError===', e)
except urllib.error.URLError as e:
# URL 存在错误
print('URLError===', e)
cookie登录
爬取微博个人信息页
# 场景: 如何绕过登录, 获取页面数据
# cookie + referer 可以应对60%的网站
import urllib.request
url = 'https://weibo.cn/6451491586/info'
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36",
# cookie中携带者身份信息, 可以访问任意页面,
# 如果不带着cookie, 一般都会被重定向到登录页面, 有些网站的登录页面不是utf-8编码, 对新手造成困扰, 这也是一种小的反爬手段
"cookie": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
# referer用于告诉服务器, 该请求是从哪个页面过来的, 一般用作图片放盗链
"referer": "https://weibo.cn/",
}
# 请求对象的定制
request = urllib.request.Request(url=url, headers=headers)
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(request)
# 获取响应的数据
content = response.read().decode('utf-8')
#将数据保存到本地
with open('weibo.html','w',encoding='utf-8')as fp:
fp.write(content)
Handler处理器
- 为什么要学习handler?
- urllib.request.urlopen(url) 不能定制请求头
- urllib.request.Request(url,headers,data) 可以定制请求头
- Handler
-
- 定制更高级的请求头 , 随着业务逻辑的复杂, 请求对象的定制已经满足不了我们的需求
- 动态cookie和代理技术需要Handler
-
基础语法
需求 使用handler来访问百度 获取网页源码
import urllib.request
url = 'http://www.baidu.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}request = urllib.request.Request(url = url,headers = headers)
核心步骤
handler build_opener open
(1)获取hanlder对象
handler = urllib.request.HTTPHandler()
(2)获取opener对象
opener = urllib.request.build_opener(handler)
(3) 调用open方法
response = opener.open(request)
content = response.read().decode('utf-8')
print(content)

代理服务器
- 代理的常用功能?
- 突破自身IP访问限制,访问国外站点。
- 访问一些单位或团体内部资源
-
- 某大学FTP(前提是该代理地址在该资源的允许访问范围之内),使用教育网内地址段免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。
- 提高访问速度
-
- 扩展: 通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户再访问相同的信息时,则直接由缓冲区中取出信息,传给用户,以提高访问速度。
- 隐藏真实IP
-
- 扩展: 上网者也可以通过这种方法隐藏自己的IP,免受攻击。
- 代码配置代理
- 创建Reuqest对象
- 创建ProxyHandler对象
- 用handler对象创建opener对象
- 使用opener.open函数发送请求
- 免费的代理:

-
代码示例
import urllib.request
url = 'http://www.baidu.com/s?wd=ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}请求对象的定制
request = urllib.request.Request(url = url,headers= headers)
不使用代理的话就直接发送请求了
response = urllib.request.urlopen(request)
proxies = {
# 'http':'118.24.219.151:16817',
'http':'114.231.172.127:22710'
}handler build_opener open
handler = urllib.request.ProxyHandler(proxies = proxies)
opener = urllib.request.build_opener(handler)
response = opener.open(request)
获取响应的信息
content = response.read().decode('utf-8')
保存
with open('data/daili.html','w',encoding='utf-8')as fp:
fp.write(content) -
代理池
import urllib.request
proxies_pool = [
{'http':'118.24.219.151:16817'},
{'http':'221.227.180.231:18620'},
]import random
proxies = random.choice(proxies_pool)
url = 'http://www.baidu.com/s?wd=ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36'
}request = urllib.request.Request(url = url,headers=headers)
handler = urllib.request.ProxyHandler(proxies=proxies)
opener = urllib.request.build_opener(handler)
response = opener.open(request)
content = response.read().decode('utf-8')
with open('daili.html','w',encoding='utf-8')as fp:
fp.write(content)