1.urllib库1.1 基本使用 使用urllib来获取百度首页的源码import urllib.request # 1、定义一个url 就是你要访问的地址 url = "http://www.baidu.com" # 2、模拟浏览器向服务器发送请求 response响应 response = urllib.request.urlopen(url) # 3、获取响应中的页面的源码 content = response.read().decode("utf-8") # 4、打印数据 print(content) read方法,返回的是字节形式的二进制数据,我们要将二进制的数据转换为字符串,需解码 : decode(‘编码的格式’)1.2 1个类型和6个方法import urllib.request url = "http://www.baidu.com" # 模拟浏览器向服务器发送请求 response = urllib.request.urlopen(url) # 一个类型:response是HTTPResponse的类型 print(type(response)) # 按照一个字节一个字节的去读 content = response.read() print(content) # 返回多少个字节 content = response.read(5) print(content) # 读取一行 content = response.readline() print(content) # 一行一行读取 直至结束 content = response.readlines() print(content) # 返回状态码 如果是200了 那么就证明我们的逻辑没有错 print(response.getcode()) # 返回的是url地址 print(response.geturl()) # 获取是一个状态信息 print(response.getheaders()) 一个类型:HTTPResponse六个方法: read、readline、readlines、getcode、geturl、getheaders1.3 下载import urllib.request # 下载网页 url_page = "http://www.baidu.com" # url代表的是下载的路径 filename文件的名字 urllib.request.urlretrieve(url_page,"baidu.html") # 下载图片 url_img = "https://img1.baidu.com/it/u=3004965690,4089234593&fm=26&fmt=auto&gp=0.jpg" urllib.request.urlretrieve(url= url_img,filename="lisa.jpg") # 下载视频 url_video = "https://vd3.bdstatic.com/mda-mhkku4ndaka5etk3/1080p/cae_h264/1629557146541497769/mda-mhkku4ndaka5etk3.mp4?v_from_s=hkapp-haokan-tucheng&auth_key=1629687514-0-0-7ed57ed7d1168bb1f06d18a4ea214300&bcevod_channel=searchbox_feed&pd=1&pt=3&abtest=" urllib.request.urlretrieve(url_video,"hxekyyds.mp4") 在python中,可以写变量的名字,也可以直接写值1.4 请求对象的定制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/92.0.4515.159 Safari/537.36" } # 因为urlopen方法中不能存储字典 所以headers不能传递进去 # 请求对象的定制 request = urllib.request.Request(url=url,headers=headers) response = urllib.request.urlopen(request) content = response.read().decode("utf8") print(content)1.5 get请求的quote方法 get请求参数,如果是中文,需要对中文进行编码,如下面这样,如果不编码会报错。 需求 获取 https://www.baidu.com/s?wd=周杰伦的网页源码编码后如下: https://www.baidu.com/s?wd=%E5%91%A8%E6%9D%B0%E4%BC%A6import urllib.request import urllib.parse 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/92.0.4515.159 Safari/537.36" } # 将周杰伦三个字变成unicode编码的格式,需要依赖于urllib.parse name = urllib.parse.quote("周杰伦") # 将转码后的字符串拼接到路径后面 url = url + name # 请求对象的定制 request = urllib.request.Request(url=url,headers=headers) # 模拟浏览器向服务器发送请求 response = urllib.request.urlopen(request) # 获取响应的内容 content = response.read().decode("utf-8") # 打印数据 print(content) quote适用于将中文转码成Unicode编码1.6 get请求的urlencode方法 urlencode应用场景:多个参数的时候。如下 https://www.baidu.com/s?wd=周杰伦&sex=男# 获取https://www.baidu.com/s?wd=%E5%91%A8%E6%9D%B0%E4%BC%A6&sex=%E7%94%B7的网页源码 import urllib.request import urllib.parse base_url = "https://www.baidu.com/s?" data = { "wd":"周杰伦", "sex":"男", "location":"中国台湾省" } new_data = urllib.parse.urlencode(data) # 请求资源路径 url = base_url + new_data 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) # 获取网页源码的数据 content = response.read().decode("utf-8") # 打印数据 print(content)1.7 post请求import urllib.request import urllib.parse 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/92.0.4515.159 Safari/537.36" } data = { "kw":"spider" } # post请求的参数,必须要进行编码 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") # 字符串--》json对象 import json obj = json.loads(content) print(obj) post请求的参数 必须要进行编码:data = urllib.parse.urlencode(data)undefined编码之后 必须调用encode方法 : data = urllib.parse.urlencode(data).encode(‘utf-8’)undefinedpost的请求的参数,是不会拼接在url的后面的 ,而是需要放在请求对象定制的参数中:undefined request = urllib.request.Request(url=url,data=data,headers=headers)1.8 异常import urllib.request import urllib.error url = "http://www.doudan1.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" } 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.9 handler 为什么要学习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; 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)1.10 代理 为什么需要代理?因为有的网站是禁止爬虫的,如果用真实的ip去爬虫,容易被封掉。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" } # 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("daili.html","w",encoding="utf-8")as fp: fp.write(content) 代理可以使用:快代理。可以使用代理池来代替一个代理 2.解析技术2.1 xpathxpath安装及加载 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’) 5.解析获取DOM元素 html_tree.xpath(xpath路径) 按照xpath的chrome插件,使用 ctrl + shift + x 打开插件xpath基本语法 1.路径查询 //:查找所有子孙节点,不考虑层级关系undefined/ :找直接子节点 2.谓词查询 //p@idundefined//p@id="maincontent" 3.属性查询 //@class 4.模糊查询 //pcontains(@id, "he")undefined//pstarts‐with(@id, "he") 5.内容查询 //p/h1/text() 6.逻辑运算 //p@id="head" and @class="s_down"undefined//title | //price 示例:from lxml import etree # xpath解析本地文件 tree = etree.parse("test.html") # 查找ul下面的li li_list = tree.xpath("//body/ul/li") # 查找所有有id的属性的li标签,text()获取标签中的内容 li_list = tree.xpath("//ul/li[@id]/text()") # 找到id为l1的li标签 注意引号的问题 li_list = tree.xpath("//ul/li[@id="l1"]/text()") # 查找到id为l1的li标签的class的属性值 li = tree.xpath("//ul/li[@id="l1"]/@class") # 查询id中包含l的li标签 li_list = tree.xpath("//ul/li[contains(@id,"l")]/text()") # 查询id的值以l开头的li标签 li_list = tree.xpath("//ul/li[starts-with(@id,"c")]/text()") #查询id为l1和class为c1的 li_list = tree.xpath("//ul/li[@id="l1" and @class="c1"]/text()") li_list = tree.xpath("//ul/li[@id="l1"]/text() | //ul/li[@id="l2"]/text()")2.2 JsonPath JsonPath只能解析本地文件。jsonpath的安装及使用 pip安装: pip install jsonpath jsonpath的使用: obj = json.load(open(‘json文件’, ‘r’, encoding=‘utf‐8’))undefinedret = jsonpath.jsonpath(obj, ‘jsonpath语法’) 示例:{ "store": { "book": [ { "category": "修真", "author": "六道", "title": "坏蛋是怎样练成的", "price": 8.95 }, { "category": "修真", "author": "天蚕土豆", "title": "斗破苍穹", "price": 12.99 }, { "category": "修真", "author": "唐家三少", "title": "斗罗大陆", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "修真", "author": "南派三叔", "title": "星辰变", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "author": "老马", "color": "黑色", "price": 19.95 } } } 解析上面的json数据import json import jsonpath obj = json.load(open("jsonpath.json","r",encoding="utf-8")) # 书店所有书的作者 author_list = jsonpath.jsonpath(obj,"$.store.book[*].author") # 所有的作者 author_list = jsonpath.jsonpath(obj,"$..author") # store下面的所有的元素 tag_list = jsonpath.jsonpath(obj,"$.store.*") # store里面所有东西的price price_list = jsonpath.jsonpath(obj,"$.store..price") # 第三个书 book = jsonpath.jsonpath(obj,"$..book[2]") # 最后一本书 book = jsonpath.jsonpath(obj,"$..book[(@.length-1)]") # 前面的两本书 book_list = jsonpath.jsonpath(obj,"$..book[0,1]") book_list = jsonpath.jsonpath(obj,"$..book[:2]") # 条件过滤需要在()的前面添加一个? # 过滤出所有的包含isbn的书。 book_list = jsonpath.jsonpath(obj,"$..book[?(@.isbn)]") # 哪本书超过了10块钱 book_list = jsonpath.jsonpath(obj,"$..book[?(@.price>10)]")2.3 BeautifulSoup基本介绍BeautifulSoup简称:bs4什么是BeatifulSoup? BeautifulSoup,和lxml一样,是一个html的解析器,主要功能也是解析和提取数据优缺点 缺点:效率没有lxml的效率高 优点:接口设计人性化,使用方便安装以及创建安装 pip install bs4 -i https://pypi.douban.com/simple导入 from bs4 import BeautifulSoup创建对象服务器响应的文件生成对象 soup = BeautifulSoup(response.read().decode(), ‘lxml’)本地文件生成对象 soup = BeautifulSoup(open(‘1.html’), ‘lxml’) 注意:默认打开文件的编码格式gbk所以需要指定打开编码格式节点定位 1.根据标签名查找节点 soup.a # 只能找到第一个a soup.a.name soup.a.attrs 2.函数find(返回一个对象) find(‘a’):只找到第一个a标签 find(‘a’, title=‘名字’) find(‘a’, class_=‘名字’)find_all(返回一个列表) find_all(‘a’) :查找到所有的a find_all([‘a’, ‘span’]) 返回所有的a和span find_all(‘a’, limit=2) 只找前两个aselect(根据选择器得到节点对象)【 】element:p.class:.firstname#id:#firstname属性选择器:undefined[attribute]:li = soup.select(‘li[class]’)undefined[attribute=value]:li = soup.select(‘li[class="hengheng1"]’)层级选择器:undefinedp p 后代选择器undefinedp>p 子代选择器:某标签的第一级子标签undefinedp,p p或p标签的所有的对象节点信息获取节点内容:适用于标签中嵌套标签的结构 obj.string obj.get_text()【推荐】节点的属性 tag.name:获取标签名 tag.attrs:将属性值作为一个字典返回获取节点属性 obj.attrs.get(‘title’)【常用】 obj.get(‘title’) obj[‘title’] 示例:Title
呵呵呵 使用BeautifulSoup解析上面的htmlfrom bs4 import BeautifulSoup # 默认打开的文件的编码格式是gbk,所以在打开文件的时候需要指定编码 soup = BeautifulSoup(open("bs4的基本使用.html",encoding="utf-8"),"lxml") # 根据标签名查找节点,找到的是第一个符合条件的数据 print(soup.a) # 获取标签的属性和属性值 print(soup.a.attrs) # bs4的一些函数 # (1)find:返回的是第一个符合条件的数据 print(soup.find("a")) # 根据title的值来找到对应的标签对象 print(soup.find("a",title="a2")) # 根据class的值来找到对应的标签对象 注意的是class需要添加下划线 print(soup.find("a",class_="a1")) # (2)find_all 返回的是一个列表,并且返回了所有的a标签 print(soup.find_all("a")) # 如果想获取的是多个标签的数据 那么需要在find_all的参数中添加的是列表的数据 print(soup.find_all(["a","span"])) # limit的作用是查找前几个数据 print(soup.find_all("li",limit=2)) # (3)select(推荐) # select方法返回的是一个列表,并且会返回多个数据 print(soup.select("a")) # 可以通过.代表class 我们把这种操作叫做类选择器 print(soup.select(".a1")) print(soup.select("#l1")) # 属性选择器:通过属性来寻找对应的标签 # 查找到li标签中有id的标签 print(soup.select("li[id]")) # 查找到li标签中id为l2的标签 print(soup.select("li[id="l2"]")) # 层级选择器 # 后代选择器:找到的是p下面的li print(soup.select("p li")) # 子代选择器:某标签的第一级子标签 print(soup.select("p > ul > li")) # 找到a标签和li标签的所有的对象 print(soup.select("a,li")) # 获取节点内容 obj = soup.select("#d1")[0] # 如果标签对象中,只有内容,那么string和get_text()都可以使用 # 如果标签对象中,除了内容还有标签,那么string就获取不到数据 而get_text()是可以获取数据 # 推荐使用get_text() print(obj.string) print(obj.get_text()) # 节点的属性 obj = soup.select("#p1")[0] # name是标签的名字 print(obj.name) # 将属性值左右一个字典返回 print(obj.attrs) # 获取节点的属性 print(obj.attrs.get("class")) print(obj.get("class")) print(obj["class"])