爬虫基础
-
使用GET请求时可以使用params添加参数【注:参数要写成字典】
-
import requests data = { 'name': 'germey', 'age': 22 } r = requests.get("http://httpbin.org/get", params=data) print(r.text)
-
-
当返回json格式时,可以使用.json()将json转化为字典
-
import requests r = requests.get("http://httpbin.org/get") print(type(r.text)) print(r.json()) print(type(r.json()))
-
-
获取cookie【.cookie】
-
import requests r = requests.get("http://httpbin.org/get") print(r.cookie)
-
-
请求头
-
User-Agent
# 使用User-Agent时,注意构造成字典格式,然后传入headers import requests headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36' } r = requests.get("https://www.zhihu.com/explore", headers=headers) print(r.text)
-
-
爬取二进制文件
-
想爬图片、音频、视频等文件,其本质都是二进制编码组成的,想要爬取,就得先拿到其二进制码
import requests r = requests.get("https://github.com/favicon.ico") print(r.text) # 将图片转化为字符串,会出现乱码 print(r.content) # 将图片转化为二进制文件
-
-
POST请求
-
将请求换为POST,然后将要传递的数据写入字典,再传入data
-
import requests data = {'name': 'germey', 'age': '22'} r = requests.post("http://httpbin.org/post", data=data) print(r.text)
-
提交json格式的内容
-
注:json格式用双引号
# -*- coding:utf-8 -*- import requests import json host = "http://httpbin.org/" endpoint = "post" url = ''.join([host,endpoint]) data = { "sites": [ { "name":"test" , "url":"https://blog.csdn.net/weixin_46211269?spm=1000.2115.3001.5343" }, { "name":"google" , "url":"https://blog.csdn.net/weixin_46211269/article/details/120703631?spm=1001.2014.3001.5501" }, { "name":"weibo" , "url":"https://blog.csdn.net/weixin_46211269/article/details/120659923?spm=1001.2014.3001.5501" } ] } r = requests.post(url,json=data) # 直接传入字符串 # r = requests.post(url,data=json.dumps(data)) # 将字符串转化为json格式后传入 response = r.json() print(response)
-
-
上传普通文件
-
普通文件也要写成字典格式
import requests import json host = "http://httpbin.org/" endpoint = "post" url = ''.join([host,endpoint]) #普通上传 files = { 'file':open('test.txt','rb') } r = requests.post(url,files=files) print (r.text)
-
-
爬虫进阶
-
回话维持
-
import requests r = requsets.Session() r.get("http://httpbin.org/cookies/set/number/123456789") c = r.get('http://httpbin.org/cookies') print(c.text)
-
-
SSL验证
-
当报有SSLError时,表示证书错误
-
通过忽略警告
import requests from requests.packages import urllib3 urllib3.disable_warnings() response = requests.get('https://www.12306.cn', verify=False) print(response.status_code)
-
通过捕捉警告到日志
import logging import requests logging.captureWarnings(True) response = requests.get('https://www.12306.cn', verify=False) print(response.status_code)
-
当然,我们也可以指定一个本地证书用作客户端证书,这可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组
import requests response = requests.get('https://www.12306.cn', cert=('/path/server.crt', '/path/key')) # 需要ctr和key文件,且key是解密状态 print(response.status_code)
-
-
-
代理设置
-
通过设置proxies参数来进行代理设置
-
proxies:写成字典,keys是请求协议,values是ip
import requests proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } requests.get('https://www.taobao.com', proxies=proxies)
-
-
-
超时设置
-
# 在request中直接添加timeout参数 import requests requests.get('https://www.taobao.com',timeout=1) # 超过1s没有响应就抛出异常,包括连接和读取,是这两个的总和
-
-
身份验证
-
基本格式,如果出现SSL验证则设置verify=False
-
import requests from requests.auth import HTTPBasicAuth r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password')) #username传入账户,password传入密码 print(r.status_code) # 出现SSLError import requests from requests.auth import HTTPBasicAuth r = requests.get('http://localhost:5000', auth=HTTPBasicAuth('username', 'password'),verify=False) print(r.status_code)
-
摘要式身份验证
import requests from requests.auth import HTTPDigestAuth url = 'http://httpbin.org/digest-auth/auth/user/pass' requests.get(url, auth=HTTPDigestAuth('user', 'pass'))
-
爬虫编码
import requests url = 'http://www.baidu.com' response = requests.get(url=url) print(response.encoding) # ISO-8859-1 response.encoding = response.apparent_encoding print(response.encoding) # utf-8
-
encoding是从http中的header中的charset字段中提取的编码方式,若header中没有charset字段则默认为ISO-8859-1编码模式,则无法解析中文,这是乱码的原因
-
apparent_encoding会从网页的内容中分析网页编码的方式,所以apparent_encoding比encoding更加准确。当网页出现乱码时可以把apparent_encoding的编码格式赋值给encoding
爬取二进制文件
# 爬取图片 import requests url = 'https://tse2-mm.cn.bing.net/th/id/OIP-C.XQzISsWklI6N2WY4wwyZSwHaHa?pid=ImgDet&rs=1' response = requests.get(url) data = response.content with open("mp4/ZJ_img.jpg", "wb") as f: f.write(data) print('下载完成')
关键字搜索
-
找到输入最关键的信息
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=张杰&fenlei=256&rsv_pq=b69ed36a003910bd&rsv_t=d364nzDVJPjYRhUCg2NW3HEyPV4cJAM310HrJImLiijOHAIdA1hk8b4FqvQ&rqlang=cn&rsv_dl=tb&rsv_enter=1&rsv_sug3=4&rsv_sug1=3&rsv_sug7=101&rsv_sug2=0&rsv_btype=i&prefixsug=%E5%BC%A0%E6%9D%B0&rsp=5&inputT=1125&rsv_sug4=2482&rsv_sug=1
这里面不是所有信息都有用一个一个删除,删除某些信息之后,页面没有改变,则这个参数就是可删除的,如果改变则为不可删除
找到
https://www.baidu.com/s?wd=张杰
最关键,在构造params时,key就得用wd,且需要在https://www.baidu.com/
加上simport requests header = { "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32" } keywords = input("请输入:") params = { "wd": keywords } url = "https://www.baidu.com/s" response = requests.get(url=url, params=params, headers=header) content = response.text filename = keywords + ".html" with open(filename, "w", encoding='utf-8') as f: f.write(content)
正则表达式
-
修饰符re.S
-
./是匹配除了换行符以为的所有字符,加上re.S就会匹配所有字符
-
修饰符 描 述 re.I 使匹配对大小写不敏感 re.L 做本地化识别(locale-aware)匹配 re.M 多行匹配,影响 ^ 和 $ re.S 使.匹配包括换行在内的所有字符 re.U 根据 Unicode 字符集解析字符。这个标志影响 \w、\W、\b 和 \B re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解
-
Xpath
-
导入模块
from lxml import tree
-
对HTML进行修正后输出【etree.HTML(), etree.tostring(),etree.decode()】
from lxml import etree text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-inactive"><a href="link3.html">third item</a></li> <li class="item-1"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a> </ul> </div> ''' html = etree.HTML(text) # 对HTML进行修正 result = etree.tostring(html) # 转化为修正后的html但类型是bytes print(result.decode('utf-8')) # 转化为str类型
-
也可以直接读取文本文件进行解析
from lxml import etree html = etree.parse('./test.heml',etree.HTMLParser()) result = etree.tostring(html) print(result.decode('utf-8'))
-
-
获取所有结点【使用//方法】
-
# 获取所有结点 from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//*') print(result) # 获取所有li结点 from lxml import etree html = etree.parse('./test.html', etree.HTMLParser()) result = html.xpath('//li') print(result)
-
-
获取子结点
-
通过 / 或 // 即可查找元素的子节点或子孙节点。假如现在想选择 li 节点的所有直接 a 子节点
# 查找li结点下的所有a子结点 from lxml import etree html = etree.parse("./test.html",etree.HTMLParser()) result = html.xpath("//li/a") # 查找li结点下的所有a子孙结点 from lxml import etree html = etree.parse("./test.html",etree.HTMLParser()) result = html.xpath("//li//a")
-
-
获取父节点
-
..
获取父节点 -
from lxml import etree text = ''' <div> <ul class="item-0"> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-inactive"><a href="link3.html">third item</a></li> <li class="item-1"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a> </ul> </div> ''' html_0 = etree.HTML(text) result = html_0.xpath('//a[@href="link3.html"]/../@class') result
-
parent::*
获取父节点【这里的*传入的的结点选择器,传入*
表示获取所有结点】 -
from lxml import etree text = ''' <div> <ul class="item-0"> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-inactive"><a href="link3.html">third item</a></li> <li class="item-1"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a> </ul> </div> ''' html_0 = etree.HTML(text) result = html_0.xpath('//a[@href="link3.html"]/parent::*/@class') result
-
-
属性匹配
-
用 @符号进行属性过滤
-
# 选取li标签中属性为'item-0' result = html_0.xpath('//li[@class="items-0"]')
-
-
文本获取
-
text()
-
result = html_0.xpath('//li[@class="items-0"]/text()') # ['\n '] # 因为text前面是/,选取直接子节点,而其直接自己结点都是a标签,文本都是在a标签内部,这里匹配到的结果就是被修正的 li 节点内部的换行符,因为自动修正的 li 节点的尾标签换行了
-
<li class="item-0"><a href="link1.html">first item</a></li> <li class="item-0"><a href="link5.html">fifth item</a> </li>
-
result = html.xpath('//li[@class="item-0"]/a/text()') print(result) # ['first item', 'fifth item']先选取了 li 节点,又利用 / 选取了其直接子节点 a,然后再选取其文本
-
result = html.xpath('//li[@class="item-0"]//text()') print(result) # ['first item', 'fifth item', '\n ']选取所有子孙节点的文本,其中前两个就是 li 的子节点 a 节点内部的文本,另外一个就是最后一个 li 节点内部的文本,即换行符
-
-
属性获取
-
用 @符号获取属性
-
result = html_0.xpath('//li/a/@href')
-
注:属性匹配
result = html_0.xpath('//li[@class="items-0"]')
与属性获取result = html_0.xpath('//li/a/@href')
要分开来
-
-
属性多值匹配
-
from lxml import etree text = ''' <li class="li li-first"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[@class="li"]/a/text()') print(result) # []
-
li 中有两个属性 li 与li-first
-
使用contains方法进行匹配,第一个参数传入属性名称,第二个参数传入属性值,只要此属性包含所传入的属性值,就可以完成匹配了。
# 使用contains方法 result = html.xpath('//li[contains(@class,"li")]/a/text()')
-
-
-
多属性匹配
-
根据多个属性确定一个节点,这时就需要同时匹配多个属性。此时可以使用运算符 and 来连接
-
text = ''' <li class="li li-first" name="item"><a href="link.html">first item</a></li> ''' html = etree.HTML(text) result = html.xpath('//li[contains(@class="li") and @name="item"]/a/text() ')
-
-
按序选择
-
在选择的时候某些属性可能同时匹配了多个节点,但是只想要其中的某个节点,可以利用中括号传入索引的方法获取特定次序的节点
-
注:传入序号时,第一个是1而不是Python里的0
-
from lxml import etree text = ''' <div> <ul> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-inactive"><a href="link3.html">third item</a></li> <li class="item-1"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a> </ul> </div> ''' html = etree.HTML(text) result = html.xpath('//li[1]/a/text()') print(result) result = html.xpath('//li[last()]/a/text()') print(result) result = html.xpath('//li[position()<3]/a/text()') print(result) result = html.xpath('//li[last()-2]/a/text()') print(result)
-
-
节点轴选择
-
ancestor轴,可以获取所有祖先结点
-
ancestor::*
获取所有祖先结点 -
ancestor::div
获取所有div祖先结点
-
-
attribute轴,可以获取所有属性值
-
atttibute::*
获取结点所有属性
-
-
child轴,获取所有子结点
-
child::*
获取所有直接子结点 -
child::a
获取所有直接a子结点
-
-
descendant轴,获取所有子孙结点
-
descendant::*
获取所有子孙结点
-
-
following轴,获取当前结点之后的结点
-
following::*
获取所有后继结点
-
-
following-sibling轴,获取所有同级结点
-
following-sibling::*
获取所有同级结点
-
-
Beautiful Soup
-
基本使用
-
html = """ <html><head><title>The Dormouse's story</title></head> <body> <p class="title" name="dromouse"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>, <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> """ from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') # 传入html字符串,指定解析器类型第二个参数 print(soup.prettify()) # 将要解析的字符串以标准缩进格式进行输出,如果html中有标签没有闭合,在实例化的时候就会自动更正格式 print(soup.title.string)
-
-
结点选择器
-
提取信息
-
获取名称【利用name属性获取结点的名称】
soup.title.name
-
获取属性【选择结点后调用attrs获取所有属性】
soup.p.attrs
返回的是字典,可根据索引来获取值
soup.p.attrs['name']
-
获取内容
soup.p.string
获取结点元素包含的文本内容 -
嵌套选择
soup.head.titile.string
-
关联选择
子结点和子孙结点
-
使用contents属性
from bs4 import BeautifulSoup soup = BeautifulSoup(text,'lxml') print(soup.ul.contents)
# 运行结果 ['\n', <li class="item-0"><a href="link1.html">first item</a></li>, '\n', <li class="item-1"><a href="link2.html">second item</a></li>, '\n', <li class="item-inactive"><a href="link3.html">third item</a></li>, '\n', <li class="item-1"><a href="link4.html">fourth item</a></li>, '\n', <li class="item-0"><a href="link5.html">fifth item</a> </li>]
如第一个 a 节点里面包含一层 span 节点,这相当于孙子节点了,但是返回结果并没有单独把 span 节点选出来。所以说,contents 属性得到的结果是直接子节点的列表 不会及将单独的span标签列出来
-
同样可以使用children属性得到相应的结果
-
想要得到所有子孙结点可以调用descendants属性
父节点和祖先结点
-
获取某个结点元素的父节点,可以调用parent属性
soup.a.parent
-
获取某个结点元素的祖先节点,可以调用parent是s属性
soup.a.parents
返回列表
兄弟结点
-
next_sibling,previous_sibling,next_siblings,previous_siblings
next_sibling
返回结点的上一个兄弟元素previous_sibling
返回结点的下一个兄弟元素next_siblings
返回前面的兄弟节点previous_siblings
返回后面的兄弟结点
-
-
提取信息
-
获取文本,属性
soup.a.string
soup.a.attrs
-
-
-
-
方法选择器
-
find_all
-
即查询所有符合条件的元素
-
find_all(name , attrs , recursive , text , **kwargs)
-
根据name查询
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all(name='ul')) print(type(soup.find_all(name='ul')[0]))
-
# 返回列表结果为: [<ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul>, <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul>] <class 'bs4.element.Tag'>
-
根据attrs查询
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1" name="elements"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all(attrs={'id': 'list-1'})) print(soup.find_all(attrs={'name': 'elements'}))
-
根据text来查询
text参数用来匹配结点的文本,传入的形式可以是字符串,可以是正则表达式
import re html=''' <div class="panel"> <div class="panel-body"> <a>Hello, this is a link</a> <a>Hello, this is a link, too</a> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.find_all(text=re.compile('link'))) # ['Hello, this is a link', 'Hello, this is a link, too']
-
-
find方法,返回单个元素,也就是匹配的第一个元素,而find_all是所有匹配元素组成的列表
-
find_parents 和 find_parent:前者返回所有祖先节点,后者返回直接父节点。
-
find_next_siblings 和 find_next_sibling:前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点。
-
find_previous_siblings 和 find_previous_sibling:前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点。
-
find_all_next 和 find_next:前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。
-
find_all_previous 和 find_previous:前者返回节点前所有符合条件的节点,后者返回第一个符合条件的节点。
-
-
CSS选择器
-
html=''' <div class="panel"> <div class="panel-heading"> <h4>Hello</h4> </div> <div class="panel-body"> <ul class="list" id="list-1"> <li class="element">Foo</li> <li class="element">Bar</li> <li class="element">Jay</li> </ul> <ul class="list list-small" id="list-2"> <li class="element">Foo</li> <li class="element">Bar</li> </ul> </div> </div> ''' from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') print(soup.select('.panel .panel-heading')) # .是选择class print(soup.select('ul li')) print(soup.select('#list-2 .element')) # #是选择id print(type(soup.select('ul')[0]))
-
获取属性
from bs4 import BeautifulSoup soup = BeautifulSoup(html, 'lxml') for ul in soup.select('ul'): print(ul['id']) print(ul.attrs['id']) # list-1 # list-1 # list-2 # list-2
-
获取文本
-
属性:
soup.string
-
方法:
soup.get_text
-
-
pyquery
-
初始化
-
字符串初始化
html = ''' <div> <ul> <li class="item-0">first item</li> <li class="item-1"><a href="link2.html">second item</a></li> <li class="item-0 active"><a href="link3.html"><span class="bold">third item</span></a></li> <li class="item-1 active"><a href="link4.html">fourth item</a></li> <li class="item-0"><a href="link5.html">fifth item</a></li> </ul> </div> ''' from pyquery import PyQuery as pq doc = pq(html) print(doc('li'))
-
URL初始化
# 可以直接传入URL进行初始化 from pyquery import PyQuery as pq doc = pq('http://www.baidu.com') print(doc('title'))
-
文件初始化
from pyquery import PyQuery as pq doc = pq(filename='demo.html') print(doc('li'))
-
-
子结点
-
# find直接查找所有子孙结点 items = doc('.list') # 找到class为list的结点 lis = items.find('li')
-
# children直接查找所有子结点 lis = items.children()
-
-
父节点
-
container = items.parent()
-
-
兄弟结点
-
li = doc('.list .item-0.active') li.siblings()
-
-
获取属性
-
a = doc('.item-0.active a') a.attr('href') a.attr.href # 当是一个元素时,两个方法完全一样,但是多个元素时,也都只会返回一个,想要得到全部,要对a.items()进行遍历
-
-
获取文本
-
# 获取标签中的文本 a.text() # 获取这个结点内部的HTML a.html() # 返回li结点内所有的HTML文本
-