python数据分析之爬虫基础:爬虫介绍以及urllib详解_tn=25017023
前言
在数据分析中,爬虫有着很大作用,可以自动爬取网页中提取的大量的数据,比如从电商网站手机商品信息,为市场分析提供数据基础。也可以补充数据集、检测动态变化等一系列作用。可以说在数据分析中有着相当大的作用!
页面结构介绍
这里主要介绍HTML的一些简单结构,需要一点前端的知识,可以根据情况直接跳过。
Title 姓名 年龄 性别 张三 18 男
- 铁锅炖大鹅
- 小鸡炖蘑菇
- 锅包肉
- 奖励自己
- 睡觉
- 起床
- 读书学习
人间无解
爬虫相关概念
1、爬虫的概念:
爬虫是一种按照一定规则,自动抓取网页信息的程序或脚本。通俗解释就是爬虫就像一个小机器人,能够自动在互联网上逛网页。它可以把网页上的文字、图片、连接之类的信息抓取下来,然后人们可以用这些信息做数据分析等一系列操作。(通过程序模拟浏览器,去向服务器发送请求,获取相应信息)
2、爬虫核心:
1、爬取网页:爬取整个网页,包含了网页中的所有内容
2、解析数据:将网页中得到的数据进行解析
3、难点:爬虫和反爬虫之间的博弈
3、爬虫的用途:
一、爬虫可以搜索引擎优化。搜索引擎靠爬虫收集网页信息,有了这些信息才能在用户搜索时,提供相应的网页结果。
二、爬虫可以做市场调研。企业可以用爬虫获取竞争对手的产品价格、用户评价等信息,了解市场动态。
三、爬虫可以用来做数据收集。用于学术研究,手机学术文件、统计数据等资料;也可以手机社交媒体的数据,来分析舆情和用户喜好。
4、爬虫分类:
1、通用爬虫:
实例:百度、360、goole等搜索引擎---伯乐在线
功能:访问网页->抓取数据->数据存储->数据处理->提供检索服务
roots协议:一个约定俗成的协议,添加roots.txt文件,来抓取本网站哪些内容不可以被抓取,起不到限制作用,自己写的爬虫无需遵循。
网站排名(SEO):1、根据pagerank算法值进行排名
2、百度竞价排名(这就是看谁的钱比较多)
缺点:1、抓取的数据大多是无用的
2、不能根据用户的需求来竞逐你获取数据
2、聚焦爬虫
功能:根据需求,实现爬虫程序,抓取需要的数据
设计思路:1、确定要爬取的url(如何获取url)
2、模拟浏览器通过HTTP协议访问url,获取服务器返回的HTML(如何访问)
3、解析HTML字符串(根据一定规则提取需要的数据)如何解析
5、反爬手段:
1、User-Agent:User-Agent中文名为用户地阿里,简称UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等。
2、代理IP:西次代理、快代理
什么是高匿名、匿名和透明代理?区别是什么?
使用透明代理,对方服务器可以知道你使用了代理,并且也知道你的真实IP;使用匿名代理,对方服务器可以知道你使用了代理,但不知道你的真实IP;使用高匿名代理,对方服务器不知道你使用了代理,也不知道你的真实ID。
3、验证码访问
4、动态加载网页:网站返回的是js数据,并不是网页的真实数据,selenium驱动真实的浏览器发送请求
5、数据加密:分析js代码
urllib库的使用
1、基本使用
首先我们需要导包:
import urllib.request
然后需要定义一个url,也就是我们要访问网页的地址(域名) ,模拟浏览器想发送请求,获取到响应信息。
# 使用urllib来获取百度首页的源码import urllib.request# 定义一个url,即我们要访问的地址url = \'http://www.baidu.com\'# 模拟浏览器向服务端发送请求response = urllib.request.urlopen(url)# 获取响应中的页面源码#read()返回的数据是字节形式的二进制数据,这时我们需要decode进行解码操作content = response.read().decode(\'utf-8\')print(content)
2、urllib的一个类型和六个方法:
import urllib.requesturl = \'http://www.baidu.com\'response = urllib.request.urlopen(url)# 一个类型print(type(response)) # # 六个方法content = response.read() # 一个字节一个字节的读content = response.read(5) # 只读取前五个字节# 一行一行的读取,每次只读取一行content = response.readline()# 读取全部行content = response.readlines()# 返回状态码,200则逻辑没问题,如果是404、500是有问题的print(response.getcode())# 返回url地址print(response.geturl())# 返回状态信息,响应头print(response.getheaders())
3、资源下载
基本语法:
urllib.request.urlretrieve(url,filename)
# url代表的是下载的路径,filename代表文件的名字
import urllib.request# 下载一个网页url_page = \"http://www.baidu.com\"urllib.request.urlretrieve(url_page, \"baidu.html\")# 下载图片url_img = \"https://img1.baidu.com/it/u=2414145851,4105017234&fm=253&fmt=auto&app=138&f=JPEG?w=800&h=1067\"urllib.request.urlretrieve(url_img, \"jujingyi.jpg\")# 下载视频url_video =\"https://vdept3.bdstatic.com/mda-qkh666t7qtjd02w7/cae_h264/1731906619294350169/mda-qkh666t7qtjd02w7.mp4?v_from_s=hkapp-haokan-nanjing&auth_key=1732795382-0-0-0627520a3ef7ab8203af6dfe76c639de&bcevod_channel=searchbox_feed&pd=1&cr=0&cd=0&pt=3&logid=0182283432&vid=582295739326229165&klogid=0182283432&abtest=122021_2\"urllib.request.urlretrieve(url_video, \"shipin.mp4\")
4、请求对象的定制
UA介绍:User Agent中文名为用户代理 ,简称UA,它是一个特殊字符串头,使得服务器能够识别客户使用的操作系统及版本、CPU类型、浏览器及版本。浏览器内核、浏览器渲染引擎、浏览器语言、浏览器插件等。
语法:
request = urllib.request.Request()
import urllib.requesturl = \'https://www.baidu.com\'response = urllib.request.urlopen(url)content = response.read().decode(\'utf-8\')print(content) # 我们会发现我们得到的内容相当少,这是因为https是由ssl协议的
上述错误代码便是我们遇到的第一个反爬手段即User Agent。在这里我们说一下url的组成:
协议:http/https,https是有ssl加密的,更加安全
主机:即域名——www.baidu.com
端口号:比如http的端口号为80,https为443,MySQL为3306,oracle为1521,redis为6379,mongodb为27017等
路径:s
参数:?后面的数据
锚点
import urllib.requesturl = \'https://www.baidu.com\'headers = {\"user-agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}# 因为urlopen()方法中不能存储字典,因此headers不能传入,此时需要定制请求对象request = urllib.request.Request(url, headers=headers)response = urllib.request.urlopen(request)content = response.read().decode(\'utf-8\')print(content)
4.1、get请求方式:urllib.parse.quote()
周杰伦的网页域名:https://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&wd=%E5%91%A8%E6%9D%B0%E4%BC%A6
需求:获取https://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&wd=周杰伦的网页源码
import urllib.requesturl =\"https://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&wd=周杰伦\"headers = {\"user-agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}request = urllib.request.Request(url, headers=headers)response = urllib.request.urlopen(request)content = response.read().decode(\'utf-8\')print(content)# UnicodeEncodeError: \'ascii\' codec can\'t encode characters in position 42-44: ordinal not in range(128)
我们会发现并不能正常运行,而是报了上述错误,这是因为周杰伦三个字超出ASCII码值检索的范围,需要我们把周杰伦三个字变成Unicode编码,这个时候需要用到quote方法。
import urllib.requestimport urllib.parsename = urllib.parse.quote(\"周杰伦\")url =\"https://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&wd=name\"headers = {\"user-agent\":\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}request = urllib.request.Request(url, headers=headers)response = urllib.request.urlopen(request)content = response.read().decode(\'utf-8\')print(content)
4.2、get请求方式:urllib.parse.urlencode()
但quote方法的缺点很显然,需要一次次的对中文汉字进行转换,每次都需要调用,相当麻烦!所以我们需要一种新的方法来解决这个问题,而这个方法便是urlencode方法。应用场景:多参数需要进行Unicode编码的时候。
# 获取\"https://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&wd=周杰伦&sex=男&location=中国台湾\"网页源码import urllib.requestimport urllib.parsedata = {\"wd\":\"周杰伦\",\"sex\":\"男\",\"location\":\"中国台湾\"}new_data = urllib.parse.urlencode(data)url = \"https://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&\"+new_dataheaders = {\"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}request = urllib.request.Request(url, headers=headers)response = urllib.request.urlopen(request)content = response.read().decode(\"utf-8\")print(content)
4.3、post请求方式
POST请求是HTTP协议中的一种请求方法。
POST主要用于向指定的资源提交要被处理的数据。比如在网页表单提交数据(如用户注册、登录信息、发表评论)到服务器时,就经常会用到POST请求。和GET请求相比,POST请求传递的数据不会像GET请求一样直接显示在URL中,这让POST请求更适合传递敏感信息(如密码),因为数据不会在URL中暴露。我们以百度翻译为例:
import urllib.requestimport urllib.parseimport jsonurl = \"https://fanyi.baidu.com/sug\"headers = {\"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}data = {\"kw\":\"spider\"}# post请求的参数一定要进行编码data = urllib.parse.urlencode(data).encode(\"utf-8\") # 因为data需要是字节类型的数据,所以此时因该进行编码操作request = urllib.request.Request(url, data, headers)response = urllib.request.urlopen(request)content = response.read().decode(\"utf-8\")content = json.loads(content)print(content)
最后结果会返回一个json数据。我们可以使用json.loads()方法将json数据转化为python数据。
4.4、ajax的get请求
我们以爬取豆瓣电影第一页数据并下载到本地为例子:
import urllib.requestimport urllib.parseurl = \"https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=0&limit=20\"headers ={\"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}request = urllib.request.Request(url,headers=headers)repose = urllib.request.urlopen(request)content = repose.read().decode(\'utf-8\')# 下载数据到本地f = open(\"豆瓣.json\",\"w\",encoding=\"utf-8\")f.write(content)f.close()
那么如何批量下载更多数据呢?比如下载前十页为例,我们提供三个方法:
# ajax的get请求豆瓣电影前十页数据并保存到本地,方法一:# import urllib.request# import urllib.parse# url = \"https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&start=0&limit=200\"# headers = {\"user-agent\":# \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}# request = urllib.request.Request(url, headers=headers)# reponse = urllib.request.urlopen(request)# content =reponse.read().decode(\"utf-8\")# f = open(\"豆瓣shi.json\",\"w\",encoding=\"utf-8\")# f.write(content)# f.close()# 方法二:通过函数形式,每一页都调用一次函数以及get方法,以追加的形式把数据写入到文件# import urllib.request# import urllib.parse### def create_request(page):# base_url = \"https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&\"# headers = {\"user-agent\":# \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"}# data = {\"start\": (page - 1) * 20,# \"limit\": 20}# data = urllib.parse.urlencode(data)# url = base_url + data# request = urllib.request.Request(url, headers=headers)# response = urllib.request.urlopen(request)# content = response.read().decode(\"utf-8\")# f = open(\"douban.json\", \"a\", encoding=\"utf-8\")# f.write(content)# f.close()## if __name__ == \'__main__\':# start_page = int(input(\"请输入起始页码:\"))# end_page = int(input(\"请输入结束页码\"))# for page in range(start_page, end_page + 1):# # 每一页都有请求对象的定制# create_request(page)# 方法三:import urllib.requestimport urllib.parsedef create_request(page): base_url = \"https://movie.douban.com/j/chart/top_list?type=5&interval_id=100%3A90&action=&\" headers = {\"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\"} data = {\"start\":(page-1)*20, \"limit\":20} data = urllib.parse.urlencode(data) url = base_url + data request = urllib.request.Request(url,headers = headers) return requestdef get_content(request): response = urllib.request.urlopen(request) content = response.read().decode(\'utf-8\') return contentdef down_load(page,content): with open(\"douban\"+str(page)+\".json\",\"w\",encoding=\"utf-8\") as f: f.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) down_load(page,content)
个人建议以第三种方法来开发,因为它更加贴合于企业的项目开发。
4.5、ajax的post请求
import urllib.requestimport urllib.parsedef create_quest(page): base_url = \"https://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/128.0.0.0 Safari/537.36\" } data = { \"cname\": \"郑州\", \"pid\":None, \"pageIndex\": page, \"pageSize\": 10 } new_data = urllib.parse.urlencode(data).encode(\'utf-8\') request = urllib.request.Request(base_url, new_data, headers) return requestdef get_content(request): response = urllib.request.urlopen(request) content = response.read().decode(\'utf-8\') # import json # content = json.loads(content) # content = content[\"Table1\"] # for i in content: # position = i[\"addressDetail\"] # print(position) return contentdef down_load(content,page): with open(\"肯德基\"+str(page)+\".json\",\"w\",encoding=\"utf-8\") as f: f.write(content)if __name__ == \'__main__\': start_page = int(input(\"请输入起始页码:\")) end_page = int(input(\"请输入终止页码:\")) for page in range(start_page,end_page+1): # 请对象的定制 request = create_quest(page) # 获取网页源码 content = get_content(request) # 下载 down_load(content,page)
总体来说和上一个案例套路基本一致,把基础打牢,应该可以轻松解决!
URLError/HTTPError
简介:1、HTTPError类是URLError类的子类
2、导入的包urllib.error.HTTPError urllib.error.URLError
3、http错误:http错误是针对浏览器无法连接到服务器而增加出来的错误提示。引导并告诉浏览者该页是哪里出了问题。
4、通过urllib发送请求的时候,有可能会发送失败,这个时候如果想让你的代码更加稳定,可以通过异常捕获来优化代码
import urllib.requestimport urllib.errorurl = \"https://blog.csdn.net/2301_80109683/article/details/1439772511\"headers ={ \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\" }try: request = urllib.request.Request(url,headers=headers) response = urllib.request.urlopen(request) content = response.read().decode(\'utf-8\') print(content)except urllib.error.HTTPError as e: print(e)
Cookie登录
Cookie登录是网站用来识别用户身份的一种方式。用户首次登陆成功后,网站服务器会生成含用户登录信息的Cookie并发送浏览器保存。再次访问是,浏览器自动发送Cookie给服务器,服务器借此确认身份,使用户免重复输账号密码。其优点是方便快捷、减轻服务器负担,缺点是存在安全风险与隐私问题。
# 微薄的cookie登录,使用场景:数据采集时,需要绕过登录,然后进入到某个页面import urllib.requesturl = \"https://weibo.cn/7860977321/info\"headers ={ \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\", # cookie中携带着登录信息,如果有登陆之后的cookie,我们可以携带着cookie进入到任何页面 \'cookie\': \'_T_WM=dc6e834b939c02f2cf63eae686703cdb; SCF=AqLqfxclF7261sAej5VukiKWtyKwQC6YWhOjr-DxKfiRwoK4a_pkJ1Wynkwvwq8P2VKl0StItmmIaH0dNf_4sok.; SUB=_2A25KSEBRDeRhGeFG7VIY9ynPyT2IHXVpJN2ZrDV6PUJbktANLU_3kW1NeTtdpWKPQR6WSGxb8AKZSDUfjZ1abI_S; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9W56bhEyW3jnAWOeE1zBXWuj5NHD95QN1hq71KMNe0zpWs4DqcjMi--NiK.Xi-2Ri--ciKnRi-zNS0nceh.NS0eEeBtt; SSOLoginState=1733046273; ALF=1735638273\' }request = urllib.request.Request(url, headers=headers)response = urllib.request.urlopen(request)content = response.read().decode(\"utf-8\")# 将数据保存到本地f = open(\"weibo.html\", \"w\", encoding=\"utf-8\")f.write(content)f.close()# 如果不加cookie,我们打开HTML文件是一片空白,这是因为请求头信息不够,因此访问不成功
Handler处理器
Handler处理器用于处理各种不同类型的URL请求。他是构建自定义网络请求处理流程的关键部分,通过不同类型的handler可以实现对HTTP、HTTPS、FTP等协议请求的定制化操作,包括处理Cookie、代理、认证等功能。
# handler处理器基本使用,使用handler访问百度来获取网页源码import urllib.requesturl = \'http://www.baidu.com\'headers ={ \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\" }request = urllib.request.Request(url, headers=headers)# 获取handler对象handler = urllib.request.HTTPHandler()# 通过handler获取openr对象opener = urllib.request.build_opener(handler)# 调用open方法response = opener.open(request)content = response.read().decode(\'utf-8\')print(content)
代理服务器
代理服务器的常用功能?
1、突破自身IP访问限制
2、访问一些单位或团体内部资源
比如某大学FTP(前提是该代理地址在该资源的允许范围之内),使用教育网内地址免费代理服务器,就可以用于对教育网开放的各类FTP下载上传,以及各类资料查询等共享服务。
3、提高放访问速度
通常代理服务器都设置一个较大的硬盘缓冲区,当有外界的信息通过时,同时也将其保存到缓冲区中,当其他用户在访问相同信息时,则直接有缓冲区中取出信息,传给用户,以提高访问速度。
4、隐藏真实IP
上网者也可以通过这种方法隐藏自己的IP,免受攻击
代码的配置代理:
1、创建Request对象
2、创建ProxyHandler对象
3、用Handler对象创建openr对象
4、使用opener.open的函数发送请求
我们可以在快代理等平台,购买一个独享代理,这样来爬取数据,即使本机IP被封,我们依然可以使用代理IP来取访问某个网站,并爬取数据。
import urllib.requesturl = \"http://www.baidu.com/s?tn=25017023_13_dg&ch=1&ie=utf-8&wd=ip\"headers ={ \"user-agent\": \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36\" }request = urllib.request.Request(url, headers=headers)proxies = { \"http\":\"106.227.10.176:16817\"}handler = urllib.request.ProxyHandler(proxies=proxies)opener = urllib.request.build_opener(handler)response = opener.open(request)content = response.read().decode(\'utf-8\')f = open(\"daili.html\", \"w\", encoding=\"utf-8\")f.write(content)f.close()
即使我们使用了代理IP,但是如果我们在短时间内高频次的去访问某一个网站,也是避免不了被封的风险的!这个时候就需要代理池(有一堆高密的代理IP)。我们可以自己做一个简易粗糙的代理池:
proxies_pool = [ { \"http\":\"106.227.10.176:168171\" }, { \"http\":\"106.227.10.176:1681712\" }, { \"http\":\"106.227.10.176:16817123\" }]# 假设这些都是真实有效的独享IP,利用随机的特性,来实现简易的代理池import randomproxies = random.choice(proxies_pool)print(proxies) # 这样就能出现随机的代理
本次爬虫分享就到这里,如有错误,欢迎指正!感谢观看!