一,一些基本的内容
# 什么是爬虫?
爬虫就是模拟客户端(浏览器)发送网络请求,获取响应,按照一定的规则提取数据,模拟客户端发(浏览器)送网络请求:发送和浏览器一模一样的数据,获取和浏览器一模一样的请求
# 爬虫的数据去哪了
呈现出来:展示在网页上,展示在app上
就行分析:对爬取的数据进行分析# HTTP:超文本传输协议
以明文方式进行传输
效率高,但是安全性低# HTTPS:HTTP+SSL(安全套接字层)
传输之前先进行加密,之后再进行解密获取内容
效率较低,但是安全# HTTP协议之请求
1:请求行
2:请求头
User-Agent:用户代理,对方的服务器能通过User-Agent直到当前请求对方服务器的资源的是什么浏览器
Cookie:用来存储用户的部分信息,每次请求时会携带Cookie发送给对方的服务器
3:请求体
用于携带数据
GET请求没有请求体
POST请求有请求体# HTTP协议之响应
1:响应头
Set-Cookie:对方通过该字段设置Cookie到本地
2:响应体
url地址对应的响应
# 使用超时参数
requests.get(url,headers = headers,timeout=3) # 该请求再3秒内必须返回响应,否则会报错
二,retrying模块
raise会抛出异常(自定义异常) # 如果出现错误就会一直重试 @retry def func(): print("...") # stop_max_attempt_number 最带的重试次数 @retry(stop_max_attempt_number = 5) def func(): print("ERROR") raise # stop_max_delay 失败重试最大事件,单位为毫秒,超出时间立即停止重试 @retry(stop_max_delay=1000) def func(): print("重试执行该方法的时间为1秒,超过1秒则不继续执行") raise # wait_fixed 设置失败重试的时间间隔 @retry(wait_fixed = 1000) def func(): print("该方法会一直重试,其每次重试的时间隔为秒") raise # wait_random_min, wait_random_max 设置失败重试随机性间隔时间 @retry(wait_random_min=1000, wait_random_max=3000) def func(): print("每次失败是重试的时间间隔为随机性的,且该随机数再1秒和3秒之间") # wait_exponential_multiplier-间隔时间倍数增加,wait_exponential_max-最大间隔时间 @retry(wait_exponential_multiplier=1000, wait_exponential_max=10000) def func(): print "Wait 2^x * 1000 milliseconds between each retry, up to 10 seconds, then 10 seconds afterwards" print int(time.time()) raise
三,requests 模块的使用
# requests中的参数
1 method # 请求方式,常见的请求方式有GET/POST 2 url # 将要获取的页面的url链接 3 param # 可以利用 字典/字符串/字节作为url的参数 4 data # 可以利用 字典/字符串/字节/文件最为Request请求携带的数据 5 json # 用以json为格式的数据,用来作为Request的内容 6 headers # 以字典的形式,请求中携带的一些请求头如user-agent,来模拟浏览器的访问 7 cookies # 用来存储用户的部分信息,用来进行下一次访问携带的数据 8 files # 传输文件 9 auth # 权限,以元组形式,支持HTTP认证的功能 10 timeout # 超时参数,可设置最长链接时间,或设置连接时间和内容读取时间(元组形式) 11 allow_redirects # 是否允许重定向 12 proxies # 字典类型,可设置代理服务器 13 verify # 自动进行网站证书的认证,默认为True 14 stream # 是否将获取的内容立即下载,默认为True
# request中参数的解释
# method def param_method(): requests.request(method = "get") requests.request(method = "post") # url def param_url(): requests.request( method = "get", url = "https://www.baidu.com" ) # data def param_data(): # data可以是字典的形式 requests.request( method = 'post', url = "https://www.baidu.com", data = {"k1":"v1","k2":"v2"} ) # data可以是字符串的形式 requests.request( method = 'post', url = "https://www.baidu.com", data = "k1=v1; k2=v2;" ) # data可以是以字节的形式 # 待补充 # data可以是以文件的形式 requests.request( method = 'post', url = "https://www.baidu.com", data = open('test.py', mode='r', encoding='utf-8'), # 文件内容是:k1=v1;k2=v2 ) # json def param_json(): # 将json中对应的数据进行序列化成一个字符串,json.dumps(...) # 然后发送到服务器端的body中,并且Content-Type是 {'Content-Type': 'application/json'} post_dict = { "BaseRequest": { "DeviceID": "e155674455601272", "Sid": TICKET_DICT["wxsid"], "Skey": TICKET_DICT["skey"], "Uin": TICKET_DICT["wxuin"], }, "SyncKey":USER_INIT_DICT["SyncKey"], "rr":1 } requests.request( method = 'POST', url = "https://www.baidu.com" json = post_dict ) # headers def param_headers(): headers = { "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36", "Host":"www.baidu.com" } requests.request( method = "GET", url = "https://www.baidu.com", headers = headers ) # cookies def param_cookies(): cookie = "...sug=3; sugstore=0; ORIGIN=0;..." requests.request( method = "GET", url = "https://www.baidu.com", cookies = cookie ) # files 文件上传 def param_file(): requests.post( url="https://www.baidu.com", file={"files":open("images.jpg"."rb")} ) # 遇到需要认证的网站 可以使用requests中的auth实现 from requests.auth import HTTPBasicAuth, HTTPDigestAuth ret = requests.request( method = "get", url = 'https://www.baidu.com', auth=HTTPBasicAuth('user', '123')) # timeout 超时参数 requests.get(url,headers = headers,timeout=3) # 该请求再3秒内必须返回响应,否则会报错 # timeout元组类型(发送时间/读取时间) ret = requests.get(url, timeout=(5, 1)) # 是否允许重定向 ret = requests.get('https://www.baidu.com', allow_redirects=False) # proxies 代理(向代理服务器发送请求,代理服务器再向url发送请求) response = requests.get( url = url, proxies = { "http": "http://127.0.0.1:9743", # 代理服务器IP地址 "https": "https://127.0.0.1:9743", } ) # stream 流 当请求时不立即获取结果,可通过迭代的方式来获取结果 如果为false会将所有文件一下子下载下来 如果为true会一点一点的下载文件(内存不足情况下使用) def param_stream(): ret = requests.get('https://www.baidu.com', stream=True) print(ret.content) ret.close() # 迭代 from contextlib import closing with closing(requests.get('https://www.baidu.com', stream=True)) as r: # 在此处理响应。 for i in r.iter_content(): print(i) # 证书 response = requests.get( url = url, cert="test.pem", # 主动提供证书文件,证书文件引入方式 verify=False # 忽略网站证书的存在,照样能返回结果 )
四,BeautifulSoup 模块的使用
BeautifulSoup是做什么用的?
BeautifulSoup是一个模块,该模块用于接收一个HTML或XML字符串,然后将其进行格式化,之后遍可以使用他提供的方法进行快速查找指定元素,从而使得在HTML或XML中查找指定元素变得简单。
from bs4 import BeautifulSoup html_list = """ <html> <head> <title>BeautifulSoup</title> </head> <body> <div class="title"> <b>这是title中的b标签中的文本</b> <h1>这是title中h1标签中的文本</h1> </div> <div class="story"> <p>友情链接</p> <a href="https://www.baidu.com" class="link" id="link2">百度首页</a> <a href="https://fanyi.baidu.com/" class="link" id="link3">百度翻译</a> </div> </body> </html> """ # # 利用BeautifulSoup将html对象转成BeautifulSoup对象soup # # features="html.parser" 为本次处理的引擎 soup = BeautifulSoup(html_list,features="html.parser") # 利用find来获取第一个匹配的标签 tag = soup.find("a") tag = soup.find(name="a",attrs={"class":"link"},test="百度首页") # 中可有多个参数,必须都匹配才能被选中 # 获取所有匹配的标签 tag = soup.find_all("a") tag = soup.find_all(name="a",attrs={"class":"link"},test="百度首页") # # 找到第一个a标签 tag_a1 = soup.find(name = "a") # 找到所有的a标签 tag_a_all = soup.find_all(name = "a") # 找到id为link2的标签 tag_id = soup.select("#link2") # 找到class为link的标签 tag_class = soup.select(".link") ## name 标签名称操作 tag = soup.find("a") name = tag.name # 获取当前标签的名称 tag.name = "p" # 修改标签的名称 print(soup) ## 标签属性操作 tag1 = soup.find("a") attrs = tag1.attrs # 获取属性的值 href class id 的值 tag1.attrs["id"] = "newid" # 将id的值修改为newid del attrs["class"] # 将class属性删除 print(attrs) ## 获取所有的子标签 body = soup.find("body") c = body.children # 获取body中所有的子标签 print(list(c)) ## 获取所有子子孙孙标签 body1 = soup.find("body") d = body1.descendants print(list(d)) ## 将所有的子标签清空 tag = soup.find("body") tag.clear() # 留下<body></body> print(soup) ## 利用递归删除所有的标签,并获取被删除的标签 test = soup.find("body") e = test.extract() # 将body标签也删除 print(soup) print(e) # e 的值包括所有被删除的标签及其其中的内容 # 将对象转为字符串 test = soup.find("body") d = test.decode() # 获取标签及其标签中的内容,也就是包括body标签 d2 = test.decode_contents() # 只获取标签中的内容,也就是步包括body标签 print(d2) # 将对象转为字节 test = soup.find("body") e = test.encode() # 包含当前的标签 e2 = test.encode_contents() # 不含当前的标签 print(e2) ## 找到所有的a和p标签 t = soup.find_all(name=["a","p"]) ## 找到id = "link" 或者 id="link2",class也是如此 t = soup.find_all(id = ["link","link2"]) ## 找到文本文件 t = soup.find_all(text="百度首页") # get, 获取标签属性 tag = soup.find('a') v = tag.get('id') print(v) # has_attr, 检查标签是否具有该属性 tag = soup.find('a') v = tag.has_attr('id') print(v) # get_text, 获取标签内部文本内容 tag = soup.find('a') v = tag.get_text() print(v) # index, 检查标签在某标签中的索引位置 tag = soup.find('body') v = tag.index(tag.find('div')) print(v) tag = soup.find('body') for i,v in enumerate(tag): print(i,v) # is_empty_element,是否是空标签(是否可以是空)或者自闭合标签, # 判断是否是如下标签:'br' , 'hr', 'input', 'img', 'meta','spacer', 'link', 'frame', 'base' tag = soup.find('br') v = tag.is_empty_element print(v) ## 获取标签中的内容 tag = soup.find("p") print(tag.string) # 获取标签中的内容 tag.string = "修改原有的内容" # 设置内容 print(tag.string) tag = soup.find("body") a = tag.stripped_strings # 获取标签内部所有的文本信息,提取所有的文本内容 print(list(a)) ## 追加标签 tag = soup.find("body") tag.append(soup.find("a")) # 取得a标签,将其追加到body中,将原本存在的a标签删除(相当于移动) print(soup) ## 创建一个标签,并将其追加到body的末尾 from bs4.element import Tag obj = Tag(name='i',attrs={'id': 'idvalue'}) obj.string = '我是标签中的文本内容' tag = soup.find('body') tag.append(obj) print(soup) ## 在当前标签内部指定位置插入一个标签 from bs4.element import Tag obj = Tag(name='i', attrs={'id':'idvalue'}) obj.string = '我是标签中的文本内容' tag = soup.find('body') tag.insert(1, obj) # 为1表示再body中的第一个位置插入该标签,为2表示再body中第一个子标签后插入该标签 print(soup) ## insert_after,insert_before 在当前标签后面或前面插入 from bs4.element import Tag obj = Tag(name='i', attrs={'id': 'idvalue'}) obj.string = '我是标签中的文本内容' tag = soup.find('body') tag.insert_before(obj) # 在当前标签的前面插入(例如body之前) # tag.insert_after(obj) # 在当前标签的后面插入 print(soup) ## 将当前标签替换为指定标签(会将原本标签删除,再加入该标签) from bs4.element import Tag obj = Tag(name='i', attrs={'id': 'idvalue'}) obj.string = '我是标签中的文本内容' tag = soup.find('div') tag.replace_with(obj) print(soup) ## 创建标签之间的关系 tag = soup.find('div') a = soup.find('a') tag.setup(previous_sibling=a) print(tag.previous_sibling) ## wrap,将指定标签把当前标签包裹起来 from bs4.element import Tag obj1 = Tag(name='div', attrs={'id': 'it'}) obj1.string = '我是一个新来的' tag = soup.find('a') v = tag.wrap(obj1) # a标签将被新创建的div标签包裹起来 print(soup) ## 去掉当前标签,将保留其包裹的标签,该标签的子标签也会保留,该标签的文本内容也会被保留 tag = soup.find('a') v = tag.unwrap() # 将第一个a标签去掉 保留其包裹的标签 print(soup)
# BeautifulSoup中的css选择器
# 选择二所有的title标签 soup.select("title")
# 选择所有p标签中的第三个标签 soup.select("p:nth-of-type(3)")
# 选择body下的 a 标签 soup.select("body a")
# 选择html下的head下的title标签 soup.select("html head title") soup.select("span,a")
# 选择head标签下的直接title标签 soup.select("head > title")
# 选择p节点下id=link1的直接子节点 soup.select("p > #link1")
# 选择body下的所有a直接节点 soup.select("body > a")
# 选择id为link1后的class=sisiter的所有兄弟节点 soup.select("#link1 ~ .sister")
# 选择id为link1后的class=sisiter的第一个兄弟节点 soup.select("#link1 + .sister")
# 选择class=sisiter的所有节点 soup.select(".sister")
# 选择class=sisiter的所有节点 soup.select("[class="sister]")
# id为link1的节点 soup.select("#link1")
# 选择a节点,且a节点的id为link2 soup.select("a#link2")
# 选择所有的a节点,且a节点含有属性href soup.select('a[href]')
# 指定href属性值的a节点 soup.select('a[href="http://example.com/elsie"]')
# 指定href属性以指定值开头的a节点 soup.select('a[href^="http://example.com/"]')
# 指定href属性以指定结尾的所有a节点 soup.select('a[href$="tillie"]')
# 利用正则表达式进行匹配 soup.select('a[href*=".com/el"]')
待补充...