如果我们把互联网比作一张大的蜘蛛网,那一台计算机上的数据便是蜘蛛网上的一个猎物,而爬虫程序就是一只小蜘蛛,沿着蜘蛛网抓取自己想要的数据
解释1:通过一个程序,根据Url(http://www.taobao.com)进行爬取网页,获取有用信息
解释2:使用程序模拟浏览器,去向服务器发送请求,获取响应信息
1.爬取网页:爬取整个网页 包含了网页中所有得内容
2.解析数据:将网页中你得到的数据 进行解析
3.难点:爬虫和反爬虫之间的博弈
通用爬虫:
实例
百度、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.request.urlopen() 模拟浏览器向服务器发送请求
response 服务器返回的数据
response的数据类型是HttpResponse
字节‐‐>字符串
解码decode
字符串‐‐>字节
编码encode
read() 字节形式读取二进制 扩展:rede(5)返回前几个字节
readline() 读取一行
readlines() 一行一行读取 直至结束
getcode() 获取状态码
geturl() 获取url
getheaders() 获取headers
urllib.request.urlretrieve()
请求网页
请求图片
请求视频
例:urllib的基本使用
# 使用urllib 来获取百度首页的源码
import urllib.request
# 1. 定义一个url,就是要访问的地址
url = 'http://www.baidu.com'
# 2. 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(url)
# 3. 获取响应中的页面的源码
# read 方法,返回的是字节形式的二进制数据
# 我们要讲二进制的数据转换为字符串
# 二进制 --> 字符串 解码 decode('编码格式')
content = response.read().decode('utf-8')
# 4. 打印数据
print(content)
例: urllib 一个类型和6个方法
# _*_ coding: utf-8 _*_
# @Time 17:06
# @Author: He
# @File 054_爬虫_urllib_1个类型6个方法.py
# @Project python基础
import urllib.request
url = 'http://wwww.baidu.com'
# 模拟浏览器向服务器发送请求
response = urllib.request.urlopen(url)
# 一个类型和六个方法
# response 是httpResponse 类型
# print(type(response)) #
# 按照一个字节一个字节读取
# content = response.read()
# print(content)
# 返回多少个字节
# content = response.read(5)
# 读取一行
# content = response.readline()
# 一行一行读.直至读完
# content = response.readlines()
# print(content)
# 返回状态吗,如果是200,那么就证明我们的逻辑没有错
print(response.getcode())
# 返回url地址
print(response.geturl())
# 获取的是状态信息
print(response.getheaders())
# 一个类型 HTTPResponse
# 六个方法: read readline readlines getcode geturl getheaders
例: urllib 下载视频图片音乐
import urllib.request
# 下载网页
# url_page = 'http://www.baidu.com'
# url 代表的是下载的路径,filename 文件的名字
# 在python中 可以变量的名字,也可以直接写值
# urllib.request.urlretrieve(url_page,'baidu.html')
# 下载图片
# url_img = 'https://img0.baidu.com/it/u=1532195562,2972426089&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=647'
# urllib.request.urlretrieve(url_img,'img.jpg')
# 下载视频
url_video = "https://vd2.bdstatic.com/mda-kcg9f303imrgda6v/mda-kcg9f303imrgda6v.mp4"
urllib.request.urlretrieve(url_video,"dowload/template/视频_055.mp4")
UA介绍:User Agent中文名为用户代理,简称 UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU 类型、浏览器及版本。浏览器内核、浏览器渲染引擎、浏览器语言、浏览器插件等
语法:request = urllib.request.Request()
例:请求对象的定制
import urllib.request
url = 'https://www.baidu.com '
# url的组成
# http/https www.baidu.com 80/443 s wd=周杰伦 #
# 协议 主机 端口号 路径 参数 锚点
# http 80
# https 443
# mysql 3306
# oracel 1521
# redis 6379
# mongodb 2701
henders = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 因为urlopen 方法中不能存储字典,所以headers不能传递进去
# 请求对象的定制
# 注意 因为参数顺序的问题,不能直接写url和 headers 中间还有data,所以我们需要关键字传参
request = urllib.request.Request(url=url,headers = henders)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
扩展:编码的由来
编码集的演变‐‐‐
由于计算机是美国人发明的,因此,最早只有127个字符被编码到计算机里,也就是大小写英文字母、数字和一些符号,
这个编码表被称为ASCII编码,比如大写字母A的编码是65,小写字母z的编码是122。
但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,
所以,中国制定了GB2312编码,用来把中文编进去。
你可以想得到的是,全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc‐kr里,
各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。
因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。
Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。
现代操作系统和大多数编程语言都直接支持Unicode。
1.get请求方式:urllib.parse.quote()
# 需求https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=周杰伦网页源码
import urllib.request
import urllib.parse
url = 'https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd='
# 请求对象的定制是为了解决反扒的第一种手段
header= {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 将周杰伦三个字变成unicode编码的格式
# 我们需要依赖于urllib.parse
name = urllib.parse.quote('周杰伦')
url += name
request = urllib.request.Request(url=url,headers=header)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
2.get请求方式:urllib.parse.urlencode()
# urlencode应用场景:多个参数的时候
# import urllib.parse
# data :{
# 'wd': '周杰伦',
# 'sex': '男'
# }
# urllib.parse.urlencode(data)
import urllib.request
import urllib.parse
bese_url = 'https://www.baidu.com/s?'
data ={
'wd': '周杰伦',
}
new_data = urllib.parse.urlencode(data)
# 请求资源路径
url = bese_url+new_data
header= {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 请求对象的定制
print(url)
request = urllib.request.Request(url= url, headers =header)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
# eg:百度翻译
# post请求
import urllib.request
import urllib.parse
url ='https://fanyi.baidu.com/sug'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
data= {
'kw': '美女',
}
# post 请求的参数,必须要进行编码
data = urllib.parse.urlencode(data).encode('utf-8')
# post 请求的参数是不会拼接在url的后面的,而是需要放在请求对象定制的参数中
request = urllib.request.Request(url=url,data=data,headers=headers)
response = urllib.request.urlopen(request)
content=response.read().decode('utf-8')
import json
print(json.loads(content)) #{'errno': 0, 'data': [{'k': '美女', 'v': 'beauty; belle; beautiful woman; femme fatale; peri'}, {'k': '美女蛇', 'v': 'merino'}, {'k': '美女与野兽', 'v': '名. Beauty and the Beast'}]}
'''
总结:
post请求方式的参数必须编码: data = urllib.parse.urlencode(data)
编码之后 必须调用encode 方法 urllib.parse.urlencode(data).encode('utf-8')
参数是放在请求对象定制的方法中的,request = urllib.request.Request(url=url,data=data,headers=headers)
'''
总结:post和get区别?
1:get请求方式的参数必须编码,参数是拼接到url后面,编码之后不需要调用encode方法
2:post请求方式的参数必须编码,参数是放在请求对象定制的方法中,编码之后需要调用encode方法
案例练习:百度详细翻译
import urllib.request
import urllib.parse
import json
url = 'https://fanyi.baidu.com/v2transapi?from=zh&to=en'
# 写爬虫时headers这个属性必须注释掉'Accept-Encoding': 'gzip, deflate, br',
# 百度翻译的反扒手段只需要一个Cookie
headers = {
# 'Accept': '*/*',
# # 'Accept-Encoding': 'gzip, deflate, br',
# 'Accept-Language': 'zh-CN,zh;q=0.9',
# 'Connection': 'keep-alive',
# 'Content-Length': 150,
# 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cookie': 'BIDUPSID=F16CB27ACF03F17C663329DE31E4062A; PSTM=1636886727; __yjs_duid=1_55c745398f06267e3638446ca7d77d7f1636887396137; BAIDUID=6DEE7240C943288B149F4CCB41CDA797:FG=1; H_BDCLCKID_SF_BFESS=tJAqoD05JD83H48k-4QEbbQH-UnLqhQg0mOZ04n-ah02V4O9jU8hb6F_qJjCL6kHW20j0h7m3UTKsq76Wh35K5tTQP6rLtbbQJR4KKJxbnLWKtbaMPKhQfDThUJiB5OLBan7-RvIXKohJh7FM4tW3J0ZyxomtfQxtNRJ0DnjtpChbC_Rj6KbjT5yeU5eetjK2CntsJOOaCvEOt5Oy4oWK441Db5ehTbRJCcPoT6C0xKWhlvoD-Jc3M04K4o9-hvT-54e2p3FBUQJeMLxQft20b0Ee2cJL4naJmTG2n7jWhk2Dq72yhoOQlRX5q79atTMfNTJ-qcH0KQpsIJM5-DWbT8IjHCeJ6KfJJ4DoIv5b-0_HRT1Mt5Eh-cH-UnLqMtf3eOZ0l8Ktq0KMRbvjx8hblbLqJjCL6ktaeJRQnOmWIQHDUnh54j1QqtRetraQjFeJer4KKJx2PPWeIJo5t5n2h_phUJiB5OLBan7-RvIXKohJh7FM4tW3J0ZyxomtfQxtNRJ0DnjtnLhbRO4-TFKjj3Bjf5; MCITY=-131%3A; RT="z=1&dm=baidu.com&si=f5wjv2hczvs&ss=l1g7dmgn&sl=6&tt=3lh&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=elh&ul=gg8&hd=ghu"; BDRCVFR[S4-dAuiWMmn]=FZ_Jfs2436CUAqWmykCULPYrWm1n1fz; delPer=0; PSINO=2; BDRCVFR[dG2JNJb_ajR]=mk3SLVN4HKm; BDRCVFR[-pGxjrCMryR]=mk3SLVN4HKm; H_PS_PSSID=; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; BA_HECTOR=05a0ak24ah0ga52gs81h4lho20q; Hm_lvt_64ecd82404c51e03dc91cb9e8c025574=1649067782; Hm_lpvt_64ecd82404c51e03dc91cb9e8c025574=1649067782; REALTIME_TRANS_SWITCH=1; FANYI_WORD_SWITCH=1; HISTORY_SWITCH=1; SOUND_SPD_SWITCH=1; SOUND_PREFER_SWITCH=1; APPGUIDE_10_0_2=1; ab_sr=1.0.1_NGIwOGNmYzEyZjdjYjMxZjI3ZjBlYjIzZmNjZTE1ZWRkZWY1N2ViMThjODVjNGRkZDZmMGViNTQxNDQ2MzVlMmMwY2Q1MzE0ZjYyMDA3MzgwNjAzYmI1MjgxYWExNWVjZDY2NzZmOTc2NzU1NjJjYzkxNTY3NmUxZjE1NDVjNTZkOTJhZDhjMThmY2Y1NThjZjViZTNhMDkwYzViN2EzMw==',
# 'Host': 'fanyi.baidu.com',
# 'Origin': 'https://fanyi.baidu.com',
# 'Referer': 'https://fanyi.baidu.com/?aldtype=16047',
# 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
# 'X-Requested-With': 'XMLHttpRequest'
}
data = {
'from': 'zh',
'to': 'en',
'query': '美女',
'transtype': 'translang',
'simple_means_flag': 3,
'sign': 551517.821612,
'token': 'fcb141eb5cc8b83af203e132e27bcb76',
'domain': 'common',
}
# post 请求的参数必须进行编码并且调用encode
data = urllib.parse.urlencode(data).encode('utf-8')
# 请求对象的定制
request = urllib.request.Request(url=url,data=data,headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
result = json.loads(content)
print(result)
案例:豆瓣电影
获取豆瓣电影第一页的数据并且保存起来
import urllib.request
import json
headers ={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
url = "https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&tart=0"
# 1. 请求对象的定制
request = urllib.request.Request(url = url,headers= headers)
# 2. 获取响应的数据
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
#3.数据下载到本地
# open 方法默认情况下使用的是gbk的编码,如果我们要想保存汉子,那么需要在open方法中指定编码格式为utf-8
# encoding='utf-8'
# fd = open("dowload/douban.json",'w',encoding='utf-8')
# fd.write(content)
# fd.close()
with open("dowload/template/douban.json",'w',encoding='utf-8') as fp:
fp.write(content)
爬取豆瓣电影前10页数据
'''
https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend
# &page_limit=20&page_start=0
#https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend
# &page_limit=20&page_start=20
# https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend
# &page_limit=20&page_start=40
# https://movie.douban.com/j/search_subjects?
# type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=60
通过上面的地址分析 页数和page_start的参数有关系.只要更改page_start的参数,就可以获取对应页数的值
page 1 2 3 4
page_start 0 20 40 60
# 规律: page_start = (page-1)*20
'''
import urllib.parse
import urllib.request
# 下载豆瓣电影前10页的数据
# 1.请求对象的定制
# 2. 获取响应的数据
# 3. 下载数据
def create_request(page):
base_url = 'https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&'
data = {
'page_start': (page-1) *20,
'page_limit': 20
}
url = base_url + urllib.parse.urlencode(data)
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
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("dowload/template/豆瓣_" +str(page)+ '_062.json', 'w',encoding='utf-8') as fp:
fp.write(content)
# 程序的入口
if __name__ == '__main__':
start_page = int(input('请输入起始的页码'))
end_page = int(input('请输入结束的页码'))
for page in range(start_page,end_page+1):
print(page)
# 每一页都有自己的请求对象的定制
request = create_request(page)
# 获取响应的数据
content = get_content(request)
# 下载
down_load(page,content)
案例:KFC官网
'''
第一页
http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname
cname: 北京
pid:
pageIndex: 1
pageSize: 10
第二页
http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname
cname: 北京
pid:
pageIndex: 2
pageSize: 10
'''
import urllib.request
import urllib.parse
def create_request(page):
beas_url = 'http://www.kfc.com.cn/kfccda/ashx/GetStoreList.ashx?op=cname'
data = {
'cname': '北京',
'pid': '',
'pageIndex': page,
'pageSize': 10
}
data= urllib.parse.urlencode(data).encode('utf-8')
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
request = urllib.request.Request(url = beas_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 dow_load(page,content):
with open('dowload/template/kfc_' +str(page) + '_063.json','w',encoding='utf_8') as fp:
fp.write(content)
if __name__ == '__main__':
start_page = int(input("请输入起始页码"))
end_page = int(input("请输入结束页码"))
for page in range(start_page,end_page+1):
# 请求对象定制
request = create_request(page)
# 获取网络源码
content = get_content(request)
# 下载
dow_load(page,content)
简介:1.HTTPError类是URLError类的子类
2.导入的包urllib.error.HTTPError urllib.error.URLError
3.http错误:http错误是针对浏览器无法连接到服务器而增加出来的错误提示。引导并告诉浏览者该页是哪里出
了问题。
4.通过urllib发送请求的时候,有可能会发送失败,这个时候如果想让你的代码更加的健壮,可以通过try‐
except进行捕获异常,异常有两类,URLError\HTTPError
import urllib.request
import urllib.error
url = 'https://blog.csdn1.net/nav/algo'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 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:
print("系统升级中")
except urllib.error.URLError:
print("地址异常")
使用案例:
1.微博的cookie登陆
# 适用的场景:数据采集的时候 需要绕过登录 然后进入到某个页面
# 个人信息页面是utf-8 但是还报错了编码错误 因为并没有进入到个人信息页面 而是跳到了登录页面
# 那么登录页面不是utf-8 所以报错
# 什么情况下访问不成功?
# 因为请求头的信息不够,所有访问不成功
import urllib.request
url = 'https://weibo.com/u/7751984183/home'
headers = {
# ':authority': 'weibo.com',
# ':method': 'GET',
# ':path': '/ajax/log/action?type=pic&uicode=20000390&fid=232150&act_code=4288&ext=module%3A01&luicode=&t=1649144295190',
# ':scheme': 'https',
'accept': 'application/json, text/plain, */*',
# 'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
# cookie 中携带者你的登录信息,如果有登录之后的cookie 那么我们就可以携带者cookie进入到任何页面
'cookie': 'wb_view_log=1920*10801; SINAGLOBAL=9641210644196.078.1649142571515; XSRF-TOKEN=dIWOhbeDSacC_EreXg3LMydm; login_sid_t=2c3ccd62daf4320f5ff9d51da6e2c2d9; cross_origin_proto=SSL; _s_tentry=weibo.com; Apache=2376798542625.207.1649144256355; ULV=1649144256365:2:2:2:2376798542625.207.1649144256355:1649142571530; ALF=1680680282; SSOLoginState=1649144283; SUB=_2A25PT4GLDeRhGeFJ7lMY-CrNwz-IHXVsPPRDrDV8PUNbmtAKLUn1kW9Nf6CP4Ch17BiqiXZmi0tUUj0G2yzvwTbH; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9WW8eBuaUnrgjrzqHBfbLsDQ5JpX5KzhUgL.FoMNSK241hBp1he2dJLoI7RLxKBLB.BL1KH-eK.4SBtt; WBPSESS=Dt2hbAUaXfkVprjyrAZT_IAPcorLTGRl5QDle__skOlcblCUgTS-NPPrg_XMjHZHApxch19UI0usNBauurkCp4diMcajdDUtYlhBp2r27yDIkpjlJUHrnOGWpW5ZL6fIO37hGDI_olJHFExrExs5Zff2PnyEnvEbGC8ILlv4KeMC-6wHFFyyBFUrZZL0nHr5FfkjG8Avvx3Bv3NqeZWrV2w==',
# referer判断当前路径是不是由上一个路径进来的,一般情况下是做图片防盗链
'referer': 'https://weibo.com/',
'traceparent': '00-7111dd4343961846623239e6adc59855-f41338966fbb8dc9-00',
'user-agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
'x-xsrf-token': 'dIWOhbeDSacC_EreXg3LMydm'
}
request = urllib.request.Request(url=url,headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
print(content)
with open('dowload/template/微博_065.html','w',encoding='utf-8') as fp:
fp.write(content)
为什么要学习handler?
urllib.request.urlopen(url)
不能定制请求头
urllib.request.Request(url,headers,data)
可以定制请求头
Handler
定制更高级的请求头(随着业务逻辑的复杂 请求对象的定制已经满足不了我们的需求(动态cookie和代理
不能使用请求对象的定制)
# 需求: 使用handler 来访问百度获取源码
import urllib.request
url = 'http://www.baidu.com'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
request = urllib.request.Request(url=url,headers=headers)
# handler bulid_opener open
# 1 获取handler对象
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)
1.代理的常用功能?
1.突破自身IP访问限制,访问国外站点。
2.访问一些单位或团体内部资源
扩展:某大学FTP(前提是该代理地址在该资源的允许访问范围之内),使用教育网内地址段免费代理服务
器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询共享等服务。
3.提高访问速度
扩展:通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲
区中,当其他用户再访问相同的信息时, 则直接由缓冲区中取出信息,传给用户,以提高访问速度。
4.隐藏真实IP
扩展:上网者也可以通过这种方法隐藏自己的IP,免受攻击。
2.代码配置代理
创建Reuqest对象
创建ProxyHandler对象
用handler对象创建opener对象
使用opener.open函数发送请求
# 需求: 使用handler 来访问百度获取源码
import urllib.request
url = 'http://www.baidu.com/s?wd=ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
request = urllib.request.Request(url=url,headers=headers)
# handler bulid_opener open
proxies = {
# ip可百度快代理,有免費的IP可用
'http': '47.113.90.161:83',
}
# 1 获取handler对象
handler = urllib.request.ProxyHandler(proxies=proxies)
# 2 获取opener对象
opener = urllib.request.build_opener(handler)
# 3 調用open方法
response = opener.open(request)
content = response.read().decode('utf-8')
print(content)
扩展:1.代理池
proxies_pool = [
{'http': '47.113.90.161:83'},
{"http": '202.55.5.209:8090'}
]
import random
import urllib.request
proxies = random.choice(proxies_pool) # 随机从代理池中获取ip
print(proxies)
url = 'http://www.baidu.con=m?wd=ip'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
request = urllib.request.Request(url=url,headers=headers)
header = urllib.request.ProxyHandler(proxies=proxies)
opener = urllib.request.build_opener(header)
response = opener.open(request)
content = response.read().decode('utf-8')
print(content)
xpath使用:
注意:提前安装xpath插件
(1)打开chrome浏览器
(2)点击右上角小圆点
(3)更多工具
(4)扩展程序
(5)拖拽xpath插件到扩展程序中
(6)如果crx文件失效,需要将后缀修改zip
(7)再次拖拽
(8)关闭浏览器重新打开
(9)ctrl + shift + x
(10)出现小黑框
1.安装lxml库
pip install lxml ‐i https://pypi.douban.com/simple
2.导入lxml.etree
from lxml import etree
3.etree.parse() 解析本地文件
html_tree = etree.parse(‘XX.html’)
4.etree.HTML() 服务器响应文件
html_tree = etree.HTML(response.read().decode(‘utf‐8’)
4.html_tree.xpath(xpath路径)
xpath基本语法:
1.路径查询
//:查找所有子孙节点,不考虑层级关系
/ :找直接子节点
2.谓词查询
//div[@id]
//div[@id=“maincontent”]
3.属性查询
//@class
4.模糊查询
//div[contains(@id, “he”)]
//div[starts‐with(@id, “he”)]
5.内容查询
//div/h1/text()
6.逻辑运算
//div[@id=“head” and @class=“s_down”]
//title | //price
应用案例: 1.站长素材图片抓取并且下载(http://sc.chinaz.com/tupian/shuaigetupian.html)–》懒加载
例: xpath的基本使用
from lxml import etree
# xpath 解析
# 1 本地文件 etree.parse
# xpath 解析本地文件
tree = etree.parse('070_爬虫_urllib_xpath的基本使用.html')
'''
// 和/ 的区别
//:查找所有子孙节点,不考虑层级关系
/: 找直接子节点
text(): 获取标签中的内容
@: 定位标签的属性如@id @class
contains(@id,"l"): 检索模糊检索
starts-with(@id,"l"): 以什么字符开头的
and: 和
|: 或,注意不能这样写 //ul/li[@id='l1' | @id = 'l1']/text()
只能这样写 //ul/li[@id='l1']/text() | //ul/li[@id='l2']/text()
'''
# 查找ul 下面的li
li_list = tree.xpath('//body//ul//li') # 路径查询
print(li_list)
# 查找所有有ID的属性的li标签
li_list = tree.xpath('//ul//li[@id]')
print(li_list)
# 查找所有有ID的属性的li标签的内容
li_list = tree.xpath('//ul//li[@id]/text()')
print(li_list)
li_list = tree.xpath('//ul//li[@id]/text()')
# 查找id为li的l2标签
li_list = tree.xpath('//ul//li[@id="l2"]')
print(li_list)
# 查找id 为l1的li标签的class的 属性值
li = tree.xpath("//ul//li[@id='l1']/@class")
print(li)
# 模糊查询
# 查找id中包含l的li标签
li_list = tree.xpath('//ul/li[contains(@id,"l")]')
# 查询id的值以l开头的li标签
li_list = tree.xpath('//ul//li[starts-with(@id,"l")]')
# 查询id 为li 和class 为c1的
li_list = tree.xpath("//ul/li[@id='l1' and @class = 'c1']/text()")
print(li_list)
# 查询id 为li 和class 为c1的
li_list = tree.xpath(" //ul/li[@id='l1']/text() | //ul/li[@id='l2']/text()")
print(li_list)
# 2 服务器响应的数据 response.read().decode('utf-8') etree.HTML()
案例: xpath 获取百度网站的百度一下
# 1 获取网页的源码
# 2 解析 解析的服务响应的文件 etree.HTML
# 3 打印
import urllib.request
url="http://www.baidu.com"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
# 请求对象定制
request = urllib.request.Request(url=url,headers= headers)
# 模拟浏览器访问服务器
response = urllib.request.urlopen(request)
# 获取网页源码
content = response.read().decode('utf-8')
# 解析网页源码.来获取我们想要的数据
from lxml import etree
# 解析服务器响应的文件
tree = etree.HTML(content)
# 获取想要的数据,xpath 返回值是一个列表的数据
result = tree.xpath('//input[@id="su"]/@value')
print(result)
xpath应用案例: 下载站长素材前十页的图片
# 1 请求对象的定制
# 2 获取网页的源码
# 3 下载
# 需求 下载前10页的图片
# https://sc.chinaz.com/tupian/dadanrenti.html
# https://sc.chinaz.com/tupian/dadanrenti_2.html
# 分析路径_2为page页码
import urllib.request
from lxml import etree
def create_request(page):
if(page == 1):
url = 'https://sc.chinaz.com/tupian/dadanrenti.html'
else:
url = 'https://sc.chinaz.com/tupian/dadanrenti_'+str(page)+'.html'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'
}
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 dowload(content):
# 下载图片
# urllib.request.urlretrieve("图片地址","文件名字")
# 数据清洗
tree = etree.HTML(content)
name_list = tree.xpath('//div[@id="container"]//a/img/@alt')
# 一般设计图片的网站都会进行懒加载,我们要获取懒加载之前的地址
src_list = tree.xpath('//div[@id="container"]//a/img/@src2')
for i in range(len(name_list)):
print(name_list[i],src_list[i]) #优优人体大胆艺术写真图片 //scpic3.chinaz.net/Files/pic/pic9/202010/apic28507_s.jpg
url = 'https:' + src_list[i]
print(url)
urllib.request.urlretrieve(url=url,filename='./dowload/img/'+name_list[i] + '.jpg')
if __name__ == '__main__':
start_page = int(input("请输入起始页码"))
end_page = int(input("请输入结束页码"))
for page in range(start_page,end_page+1):
# 请求对象的定制
request = create_request(page)
# 获取网页的源码
content = get_content(request)
# 下载
dowload(content)
jsonpath的安装及使用方式:
pip安装:
pip install jsonpath
jsonpath的使用:
obj = json.load(open('json文件', 'r', encoding='utf‐8'))
ret = jsonpath.jsonpath(obj, 'jsonpath语法')
教程连接(http://blog.csdn.net/luxideyao/article/details/77802389)
jsonPath的基本使用
{
"store": {
"book": [
{
"category": "修真",
"author": "南派三叔",
"title": "星辰變",
"isbn": "0-395-19395-8",
"price": 22.99
},
{
"category": "修真",
"author": "唐家三少",
"title": "鬥羅大陸",
"isbn": "0-395-19395-8",
"price": 8.99
},
{
"category": "都市",
"author": "天蚕土豆",
"title": "天蚕土豆",
"price": 18.99
}
],
"bicycle": {
"author": "老马",
"color": "黑色",
"price": 19.9
}
}
}
利用JSONpath解析上面的JSON数据
import json
import jsonpath
obj = json.load(open('073_爬虫_urllib_解析_jsonpath.json','r',encoding= 'utf-8'))
# 书店所有的书的作者
author_list = jsonpath.jsonpath(obj,'$.store.book[*].author')
print(author_list)
# 书店第二本书
author_list = jsonpath.jsonpath(obj,'$.store.book[1].author')
print(author_list)
# 所有的作者
author_list = jsonpath.jsonpath(obj,'$..author')
print(author_list)
# store 下面所有的元素
author_list = jsonpath.jsonpath(obj,"$.store.*")
print(author_list)
# store 里面的所有东西的price
price_list = jsonpath.jsonpath(obj,"$.store..price")
print(price_list)
# 第三本书
price_list = jsonpath.jsonpath(obj,"$..book[2]")
print(price_list)
# 最后一本书
price_list = jsonpath.jsonpath(obj,"$..book[(@.length-1)]")
print(price_list)
# 前两本书
price_list = jsonpath.jsonpath(obj,"$..book[0,1]")
print(price_list)
price_list = jsonpath.jsonpath(obj,"$..book[:2]")
print(price_list)
# 条件过滤需要在圆括号前加一个?号
# 过滤除所有 的包含ison的书
price_list = jsonpath.jsonpath(obj,"$..book[?(@.isbn)]")
print(price_list)
# 哪本书超过了10元
book_list = jsonpath.jsonpath(obj, "$..book[?(@.price > 10)]")
print(book_list)
案例: jsonPath 解析淘票票
import urllib.request
import jsonpath
import json
url = 'https://dianying.taobao.com/cityAction.json?activityId&_ksTS=1649219126937_97&jsoncallback=jsonp98&action=cityAction&n_s=new&event_submit_doGetAllRegion=true'
headers = {
'accept': 'text/javascript, application/javascript, application/ecmascript, application/x-ecmascript, */*; q=0.01',
# 'accept-encoding': 'gzip, deflate, br',
'accept-language': 'zh-CN,zh;q=0.9',
'cookie': 'td_cookie=1358920425; t=7dbc3166374399f2591f65978bc1442e; cookie2=184a378260be631b57c2e87e585d34e6; v=0; _tb_token_=e3374b17369e8; cna=SNYXGm5rLwACAW/E96LhJaH3; xlly_s=1; tfstk=c4iNB3wb_hKZvMZXylZ21e6V4eVOZUHinMy7SVKzHVcXNbzGiNfYdxALL-O1pPf..; l=eBEEU2bmLRJj_gaFBO5Churza779fIRf1lVzaNbMiInca6Q5TFaSSNC3ORbM7dtjgtfndety4GCN_RE6WGUdbgiMW_N-1NKDmxJM-; isg=BKqqAnp4vChGOTBEcvgERLRI-xBMGy51u9HLcDRjsv2IZ0khHKnohb7Z95P7rKYN',
'referer': 'https://dianying.taobao.com/',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36',
'x-requested-with': 'XMLHttpRequest',
}
request = urllib.request.Request(url=url,headers=headers)
response = urllib.request.urlopen(request)
content = response.read().decode('utf-8')
# split切割
content = content.split('(')[1].split(");")[0]
# 将json字符串直接转换json对象,不用存到本地再读出来那么麻烦
# with open('074_爬虫_urllib_jsonpath解析淘票票.json','w',encoding='utf-8') as fp:
# fp.write(content)
# obj = json.load(open('074_爬虫_urllib_jsonpath解析淘票票.json','r',encoding='utf-8'))
obj = json.loads(content)
# 获取所有的地址名称
regionNames = jsonpath.jsonpath(obj,'$.returnValue..regionName')
print(regionNames)
1.BeautifulSoup简称:
bs4
2.什么是BeatifulSoup?
BeautifulSoup,和lxml一样,是一个html的解析器,主要功能也是解析和提取数据
3.优缺点?
缺点:效率没有lxml的效率高
优点:接口设计人性化,使用方便
1.安装
pip install bs4
2.导入
from bs4 import BeautifulSoup
3.创建对象
服务器响应的文件生成对象
soup = BeautifulSoup(response.read().decode(), 'lxml')
本地文件生成对象
soup = BeautifulSoup(open('1.html'), 'lxml')
注意:默认打开文件的编码格式gbk所以需要指定打开编码格式
1.根据标签名查找节点
soup.a 【注】只能找到第一个a
soup.a.name
soup.a.attrs
2.函数
(1).find(返回一个对象)
find('a'):只找到第一个a标签
4.节点信息
应用实例: 1.股票信息提取(http://quote.stockstar.com/)
2.中华英才网-旧版
3 .腾讯公司招聘需求抓取(https://hr.tencent.com/index.php)
find('a', title='名字')
find('a', class_='名字')
(2).find_all(返回一个列表)
find_all('a') 查找到所有的a
find_all(['a', 'span']) 返回所有的a和span
find_all('a', limit=2) 只找前两个a
(3).select(根据选择器得到节点对象)【推荐】
1.element
eg:p
2..class
eg:.firstname
3.#id
eg:#firstname
4.属性选择器
[attribute]
eg:li = soup.select('li[class]')
[attribute=value]
eg:li = soup.select('li[class="hengheng1"]')
5.层级选择器
element element
div p
element>element
div>p
element,element
div,p
eg:soup = soup.select('a,span')
(1).获取节点内容:适用于标签中嵌套标签的结构
obj.string
obj.get_text()【推荐】
(2).节点的属性
tag.name 获取标签名
eg:tag = find('li)
print(tag.name)
tag.attrs将属性值作为一个字典返回
(3).获取节点属性
obj.attrs.get('title')【常用】
obj.get('title')
obj['title']
应用实例: 1.股票信息提取(http://quote.stockstar.com/)
2.中华英才网-旧版
3 .腾讯公司招聘需求抓取(https://hr.tencent.com/index.php)
bs4的基本使用
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<div>
<ul>
<li id="l1">张三li>
<li id="l2">李四li>
<li>王五li>
<a href="" id="sgg">尚硅谷a>
<a href="" class="a1">尚硅谷1a>
<span>spanspan>
ul>
div>
<a href="" title="啊">百度a>
<div id="div2">
<span>
hahaha
span>
div>
<p id="p1" class="p1">p>
body>
html>
利用bs4 解析上面的html
from bs4 import BeautifulSoup
# 通过解析本地文件来将bs4的基本语法进行讲解
# 默认打开文件的编码格式是gbk,所以在打开文件的时候需要指定编码
soup = BeautifulSoup(open('075_爬虫_urllib_bs4的基本使用.html',encoding='utf-8'),'lxml')
# 根据标签名查找节点
# 找到的是第一个符合条件的数据
print(soup.a) # 尚硅谷
# 获取标签的属性和属性值
print(soup.a.attrs) #{'href': '', 'id': ''}
# bs4 的一些函数
# 1. find
# 返回的是第一个符合条件的数据
print(soup.find('a')) # 尚硅谷
# 根据title的值找到对应的对象
print(soup.find('a',title="啊")) #百度
# 根据class的值来找到对象的标签对象,注意的是class需要添加下划线
print(soup.find('a',class_="a1")) #尚硅谷1
# 2. findAll
# 返回的是一个列表,并且返回了所有的a标签
print(soup.findAll('a')) #[尚硅谷, 尚硅谷1, 百度]
# 如果想获取的是多个标签的数据,那么需要在find_all的参数中添加的是列表的数据
print(soup.find_all(['a','span'])) #[尚硅谷, 尚硅谷1, span, 百度]
# limit的作用是查找前几个数据
print(soup.find_all('li',limit=2)) #[尚硅谷, 尚硅谷1, span, 百度]
# 3. select
# select 方法的返回是一个列表 并且会返回多个数据
print(soup.select('a')) #[尚硅谷, 尚硅谷1, 百度]
# 可以通过.代表class ,我们吧这种操作叫做类选择器
print(soup.select('.a1')) #[尚硅谷1]
# id选择器
print(soup.select("#sgg")) #[尚硅谷]
# 属性选择器--通过属性来寻找对应的标签
# 查找到li标签中在id的标签
print(soup.select('li[id]')) #[张三 , 李四 ]
# 查找到li 标签中id为l2的标签
print(soup.select('li[id="l2"]')) #[李四 ]
# 层级选择器
# 后代选择器
# 找到div下面的li
print(soup.select('div li')) #[张三 , 李四 , 王五 ]
# 子代选择器
# 某标签的第一集子标签
# 注意:很多的计算机编程语言中,如果不加空格不会输出内容,但是在bs4中,不会报错,会显示内容
print(soup.select('div > ul > li')) #[张三 , 李四 , 王五 ]
print(soup.select('div>ul>li')) #[张三 , 李四 , 王五 ]
# 找到a标签 和li 标签所有的对象
print(soup.select('a,li'))
# 节点信息
# 回去节点内容
obj = soup.select('#div2')[0]
# 如果标签对象中只有内容,那么String和get_text()都可以使用
# 如果标签对象中,除了内容还有标签,那么string就获取不到数据,而get_text()是可以获取数据
# 我们一般情况下推荐使用get_text()
print(obj.get_text())
print(obj.string)
# 节点属性
obj = soup.select("#p1")[0]
# name 是标签的名字
print(obj.name) # p
# 将属性值作为一个字典返回
print(obj.attrs) #{'id': 'p1', 'class': ['p1']}
# 获取节点属性
obj = soup.select('#p1')[0]
print(obj.attrs.get('class')) #['p1']
print(obj.get("class")) #['p1']
print(obj['class'])#['p1']
案例: BS4 爬取星巴克菜单内的图片
import urllib.request
# 爬取星巴克菜单内的图片
import img as img
from bs4 import BeautifulSoup
url = 'https://www.starbucks.com.cn/menu/'
response = urllib.request.urlopen(url)
content = response.read().decode('utf-8')
soup = BeautifulSoup(content,'lxml')
# //ul[@class='grid padded-3 product']//strong
name = soup.select("ul[class='grid padded-3 product'] strong")
div = soup.select("ul[class='grid padded-3 product'] div[class='preview circle']")
imgUrl = "https://www.starbucks.com.cn" # img的图片路径不完整,我们需要在图片的路径上加上它的域名
def download(name,img):
print('正在下载-->' + name)
try:
urllib.request.urlretrieve(url=imgUrl+img, filename='./dowload/星巴克/' + name + '.jpg')
except Exception:
print("下载失败-->" + name)
for index,item in enumerate(div):
style = item.get('style')
img = style.split("(\"")[1].split("\")")[0] #/images/products/9oz-reserve-glass-cup.jpg
download(name[index].get_text(),img)