脚本编写相关的Python库
Requests库
requests.request(method, url, **kwargs)
是最基本的方法,其它方法封装内部大多为request
我们通常用r = requests.get(url)
获取网页信息,其会构造一个向服务器请求资源的Request对象,对象由Requests库内部生成;get()
函数返回的内容表示为r
,r
是一个Response对象,包含从服务器返回的所有相关资源。
完整方法:requests.get(url, params=None, **kwargs)
,其中,url
为链接,params
是字典或字节流格式,**kwargs
是12个控制访问的参数。
request参数(使用时写成
r.request.params
等):
params
:字典或字节序列,作为参数增加到url中data
:字典、字节序列或文件对象,作为Request的内容json
:JSON格式的数据,作为Request的内容headers
:字典,HTTP定制头files
:字典类型,传输文件timeout
:设定超时时间(单位:秒)proxies
:字典类型,设定访问代理服务器,可以增加登录认证allow_redirects
:True/False,默认为True,重定向开关stream
:True/False,默认为True,获取内容立即下载开关verify
:True/False,默认为True,认证SSL证书开关cert
:本地SSL证书路径cookies
:字典或CookieJar,Request中的cookieauth
:元组,支持HTTP认证功能
response对象:
r.status_code
:状态码,状态码为200表示访问成功,否则(比如404)表示访问失败。
r.headers
:头部信息
r.text
:url的页面内容
r.content
:url相应内容的二进制形式
r.encoding
:从HTTP header中猜测的响应内容编码方式(如果header中不存在charset,则认为编码为ISO-8859-1)
r.apparent_encoding
:从内容中分析出的相应内容编码方式(备选方式)(一般更加准确)
r.raise_for_status()
:如果状态码不是200,则产生异常requests.HTTPError
(如404表示失败)
爬取网页的通用代码框架:
import requests
def getHTMLText(url):
try:
r = requests.get(url, timeout = 30)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return "ERROR"
if __name__ == "__main__":
url = "http://www.baidu.com"
print(getHTMLText(url))
HTTP协议
HTTP,Hypertext Transfer Protocol,超文本传输协议
HTTP协议采用URL作为定位网络资源的标识。
URL格式:
http://host[:port][path]
- host:合法的Internet主机域名或IP地址
- port:端口号(默认为80)
- path:资源路径
其它方法:
requests.head()
:获取HTML网页头信息(可以用r.headers
获取头部信息的内容,但是不能使用r.text
)
requests.post(url, data = [content])
:向HTML网页提交POST请求,附加新的数据
requests.put(url, data = [content])
:向HTML网页提交PUT请求,与POST使用类似,不过功能是覆盖原URL位置的资源
requests.patch()
:向HTML网页提交局部修改请求
requests.delete()
:向HTML网页提交删除请求
Robots协议:
网站的robots协议一般放在根目录,如http://www.baidu.com/robots.txt
基本语法:
#注释:*表示所有,/表示根目录 User-agent: * (表示限制哪些爬虫) Disallow: / (表示不允许访问的目录)
修改user-agent:
网站可能通过对网站访问的HTTP的头来判断访问是否由爬虫引起,但是我们可以更改头部信息,即修改
r.request.headers
(如r = requests.get(url, headers = {'user-agent': 'Mozilla/5.0'})
)来进行访问。
搜索引擎关键字接口:
比如:http://www.baidu.com/s?wd=keyword、http://www.so.com/s?q=keyword
我们把信息加在
params
里即可:import requests kv = {'wd': 'Python'} kv1 = {'user-agent': 'Mozilla/5.0'} r = requests.get("http://www.baidu.com/s?ie=UTF-8", params = kv, headers = kv1) print(r.request.url)
输出的结果:
https://www.baidu.com/s?ie=UTF-8&wd=Python
爬取图片:
import requests import os root = "D://pics/" url = "https://tva4.sinaimg.cn/large/0072Vf1pgy1foenqvqwvqj31hc0xc7aj.jpg" path = root + url.split('/')[-1] #命名为网站上的原文件名,即最后一个'/'的后面的内容 if not os.path.exists(root): os.mkdir(root) if not os.path.exists(path): r = requests.get(url) with open(path, 'wb') as f:#打开文件,定义为标识符f f.write(r.content)##content为二进制内容 f.close() print("Picture Saved Successfully!") else: print("Picture Exists already!")
(视频和音频也是一样的)
信息标记和提取
三种信息标记:
XML(eXtensible Markup Language)(用于Internet上的信息交互):
<name ...> ... </name>
、<name ... />
、<!--...-->
JSON(JavaScript Object Notation)(用于移动应用云端和节点的信息通信,无注释):形式为键值对。如果一个键对应多个值,用[]括起来;如果有嵌套,用{}括起来。
(例:
"key": "value"
、"key": ["value1", "value2"]
、"key": {"subkey": "subvalue"}
)YMAL(YAML Ain't Markup Language)(用于系统配置文件,易读、有注释):形式为无类型键值对,即
key: value
,用缩进表示所属关系,如:name: newname: name1 oldname: name2
用
-
表示并列关系,如:name: - name1 - name2
用
#
进行注释,用|
表达大段数据。
提取信息的一般方法:
- 完整解析信息的标记形式,再提取关键信息(BeautifulSoup库)
- 直接搜索关键信息(Re库)
Beautiful Soup库
~是解析html和xml文件的功能库。
引用:from bs4 import BeautifulSoup
(简写为bs4
,导入BeautifulSoup
这个类)(注意大写)
解析:soup = BeautifulSoup(r.text, "html.parser")
即解析后的内容(会转成UTF-8)
(或者soup2 = BeautifulSoup(open("D://a.html"), "html.parser")
)
(soup.prettify()
可返回排版后的文件内容)
其它解析器:
lxml的HTML解析器:
BeautifulSoup(mk, 'lxml')
lxml的XML解析器:
BeautifulSoup(mk, 'xml')
以上两个都需要
pip install lxml
html5lib的解析器:BeautifulSoup(mk, 'html5lib')
需要
pip install html5lib
HTML代码是tag组成的树形结构,用<tag>...</tag>
括起来。
soup.<tag>
可以调用<tag>
的内容,如soup.title
,可以返回<title>TITLE_CONTENT</title>
如果存在相同名字的tag时,用soup.<tag>
只能返回第一个。
用<tag>.parent
可以调用上一层tag,<tag>.name
可以得到tag名字,如:
soup.a.name
(就是a
)
soup.a.parent.name
、soup.a.parent.parent.name
:a的祖先tag的名字
soup.attrs
获取tag的属性信息(字典类型)
soup.string
表示尖括号之间的内容(类型为bs4.element.NevigableString
)
在HTML代码中,<p><!...!></p>
表示注释,而我们调用soup.p.string
时,会得到注释的内容,而且和上面不同的是,注释的类型是bs4.element.Comment
,所以我们可以通过string
的类型判断这一段是不是注释。
tag树的遍历:
tag.contents
:子节点的列表(也包含字符串节点,比如'\n'换行酱紫的,用isinstance(tag, bs4.element.Tag)
进行判断(需要直接import bs4
))
tag.children
:子节点的迭代类型
tag.descendants
:子孙节点的迭代类型
tag.parent
:父节点(如果没有就是None
)
tag.parents
:祖先节点的迭代类型
tag.next_sibling
:按照HTML文本顺序的下一个兄弟节点
tag.previous_sibling
:按照HTML文本顺序的上一个兄弟节点
tag.next_siblings
:按照HTML文本顺序的后面的所有兄弟节点的迭代类型
tag.previous_siblings
:按照HTML文本顺序的前面的所有兄弟节点的迭代类型
tag.find_all(name, attrs, recursive, string, **kwargs)
:查找,返回列表
name
为检索的tag名字;如果要查找多种tag,可以传入一个列表;如果给出的标签名称是True
,那么会返回所有标签信息;如果我们要求查找“**开头的tag”,可以利用re库。
attrs
限制属性,可以查找属性值,也可以查找键值对,比如:soup.find_all('p', 'xxx')
或者soup.find_all(att = 'xxx')
。
recursive
:是否检索全部子孙,默认为True
(False
即只检索子节点)。
string
指对<>...</>
中间区域进行字符串检索(返回的只有中间的字符串的列表)。
简写:tag.find_all(...)
可简写作tag(...)
,soup.find_all(...)
也可以简写作soup(...)
。
扩展:
<>.find()
:只返回一个结果
<>.find_parents()
:在祖先节点中查找,列表类型
<>.find_parent()
:在祖先节点中查找,只返回一个结果
find_next_siblings()
、find_next_sibling()
、find_previous_siblings()
、find_previous_sibling()
同理。
Re库
RE(Regular Expression):正则表达式
正则表达式语法:
.
:表示任何字符
[]
:字符集,[abc]
表示a、b、c;[a-z]
表示所有小写字母
[^]
:非字符集,[^abc]
表示非a、b、c的所有字符
*
:前一个字符零次或多次扩展,如abc*
表示ab
、abc
、abcc
……
+
:前一个字符一次或更多次数的扩展,如abc+
表示abc
、abcc
……
?
:前一个字符零次或一次扩展,如abc?
表示ab
和abc
|
:左右表达式任意一个,如abc|def
表示abc
和def
{m}
:前一个字符的m次扩展,ab{2}c
表示abbc
{m,n}
:前一个字符的m~n次扩展,ab{1,2}c
表示abc
和abbc
^
:匹配字符串开头
$
:匹配字符串结尾
()
:分组标记,内部只能使用|
\d
:等价于[0-9]
\w
:等价于[A-Za-z0-9]
应用:
字母组成的字符串:
^[A-Za-z]+$
0~255的数字:
([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])
(0-99|100-199|200-249|250-255)
正则表达式的表示类型是raw string
(即不包含转义符的字符串),表示为r'text'
。
常用函数:
re.search(RawString, String, flags = 0)
:在一个字符串中搜索匹配正则表达式的第一个位置,返回match对象
flags
为控制标记,常用标记有:
re.I
:忽略大小写re.M
:^
允许把每行的开头作为匹配的开始位置re.S
:.
能匹配任意字符(原本不能匹配换行符)
re.match(RawString, String, flags = 0)
:在一个字符串的开始位置起匹配正则表达式,返回match对象
re.findall(RawString, String, flags = 0)
:以列表形式返回所有匹配的字符串
re.split(RawString, String, maxsplit = 0, flags = 0)
:将字符串按正则表达式匹配结果进行分割,将匹配的部分去掉,返回列表(maxsplit
表示最大分割数,剩余部分作为最后一个元素输出)
re.finditer(RawString, String, flags = 0)
:返回一个匹配结果的迭代类型,迭代元素为match对象
re.sub(RawString, repl, String, count = 0, flags = 0)
:将所有匹配的子串替换,返回替换后的子串(repl
表示替换字符串,count
表示最大替换次数)match对象的属性:
.string
:String.re
:RawString.pos
:正则表达式搜索文本的开始位置.endpos
:正则表达式搜索文本的结束位置match对象的方法:
.group(0)
:匹配后的字符串(留坑:group(1)
、group(2)
).start()
:匹配字符串在原始字符串的开始位置.end()
:匹配字符串在原始字符串的结束位置span()
:(.start(), .end())
贪婪匹配&最小匹配:
Re库默认采用贪婪匹配,即输出匹配最长的子串。
如果要求得到最短的子串,需要对符号做如下修改:
*?
:0次或多次扩展,取匹配的最小次数+?
:1次或多次扩展,取匹配的最小次数??
:0次或1次扩展,取匹配的最小次数{m, n}?
:m次到n次扩展,取匹配的最小次数
两种写法:
- 函数式写法:
rst = re.search(r'...', '...')
- 面向对象写法:
pat = re.compile(r'...')
rst = pat.search('...')
re.compile(r'...', flags = 0)
可以将正则表达式的原生字符串形式编译成re对象,以上六种函数都可以直接用于re对象。
Selenium库
参考:
安装
pip install selenium
在https://chromedriver.storage.googleapis.com/index.html找到对应版本的chromedriver丢到chrome.exe所在文件夹。
(写代码的时候不要把文件命名为selenium.py
!会出现ImportError: cannot import name webdriver
)
使用
大致框架
- 预备:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome('C:\Program Files\Google\Chrome\Application\chromedriver.exe')
selenium.webdriver
提供WebDriver的实现,Keys
提供案件支持,如RETURN
、F1
、ALT
如果不给chromedriver.exe
配置环境变量,就直接写绝对路径。
- 加载网页:
driver.get("http://www.python.org")
……中间部分搞一些大新闻
- 关闭网页(关闭一个标签页)
driver.close()
交互方式
如果想定位到一个可以输入的文本框,可以先在浏览器中检查网页的elements,找到对应位置的信息。
比如北航CAS登录页面的密码框的信息:
<input type="password" placeholder="请输入密码" i18n="login.form.password.placeholder" id="pwPassword" name="password">
可以这样定位:
elem = driver.find_element_by_id("pwPassword")
其它定位方式:
find_element_by_id
find_element_by_name
find_element_by_xpath #见下
find_element_by_link_text #见下
find_element_by_partial_link_text
find_element_by_tag_name #即查找HTML标签名
find_element_by_class_name #即查找class
find_element_by_css_selector #待填坑
(如果查找多个元素,改成elements
即可,返回列表类型)
关于XPATH:
举例:
<html> <body> <form id="loginForm"> <input name="username" type="text" /> <input name="password" type="password" /> <input name="continue" type="submit" value="Login" /> <input name="continue" type="button" value="Clear" /> </form> </body> <html>
三种查找form的方法:
login_form = driver.find_element_by_xpath("/html/body/form[1]") #绝对路径 login_form = driver.find_element_by_xpath("//form[1]") #页面中第一个form元素 login_form = driver.find_element_by_xpath("//form[@id='loginForm']") #id为loginForm的form元素
三种查找username的方法:
username = driver.find_element_by_xpath("//form[input/@name='username']") #第一个form中name为username的input元素 username = driver.find_element_by_xpath("//form[@id='loginForm']/input[1]") #id为loginForm的form元素的第一个input元素 username = driver.find_element_by_xpath("//input[@name='username']") #第一个name属性是username的input元素
两种获取Clear按钮的方法:
clear_button = driver.find_element_by_xpath("//input[@name='continue'][@type='button']") #name是continue,type是button的input元素 clear_button = driver.find_element_by_xpath("//form[@id='loginForm']/input[4]") #id是loginForm的form元素的第四个input元素
关于通过链接文本获取超链接:
link = driver.find_element_by_link_text('Content') link = driver.find_element_by_partial_link_text('PartialContent') #部分内容查找
模拟键盘:
elem.clear()
:将文本框清空
elem.send_keys("xxxxxxx")
:输入
elem.send_keys(Keys.RETURN)
:回车
下拉选择框:
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_name('name'))
select.select_by_index(index)
select.select_by_visible_text("text")
select.select_by_value(value)
取消选择:
select = Select(driver.find_element_by_id('id'))
select.deselect_all()
点击按钮:
driver.find_element_by_id("submit").click()
弹出对话框:
alert = driver.switch_to_alert()
alert.accept()
alert.dismiss()