好久沒用requests
了,因為一會兒要寫個簡單的爬蟲,所以還是隨便寫一點複習下。
import requests r = requests.get('https://api.github.com/user', auth=('haiyu19931121@163.com', 'Shy18137803170'))print(r.status_code) # 状态码200print(r.json()) # 返回json格式print(r.text) # 返回文本print(r.headers) # 头信息print(r.encoding) # 编码方式,一般utf-8# 当写入文件比较大时,避免内存耗尽,可以一次写指定的字节数或者一行。# 一次读一行,chunk_size=512为默认值for chunk in r.iter_lines():print(chunk)# 一次读取一块,大小为512for chunk in r.iter_content(chunk_size=512):print(chunk)
注意iter_lines
和iter_content
傳回的都是位元組數據,若要寫入文件,不管是文字還是圖片,都需要以wb
的方式開啟。
進入正題,早就聽說這個著名的庫,以前寫爬蟲用正則表達式雖然不麻煩,但有時會匹配不准確。使用Beautifulsoup可以準確地從HTML標籤中擷取資料。雖然是慢了點,但是簡單好使呀。
from bs4 import BeautifulSoup html_doc = """<html><head><title>The Dormouse's story</title></head><body><p class="title"><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>"""# 就注意一点,第二个参数指定解析器,必须填上,不然会有警告。推荐使用lxmlsoup = BeautifulSoup(html_doc, 'lxml')
緊接著上面的程式碼,看下面一些簡單的操作。 使用點屬性的行為,會得到第一個查找到的符合條件的資料。是find
方法的簡寫。
soup.a soup.find('p')
上面的兩句是等價的。
# soup.body是一个Tag对象。是body标签中所有html代码print(soup.body)
<body> <p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>; and they lived at the bottom of a well.</p> <p class="story">...</p> </body>
# 获取body里所有文本,不含标签print(soup.body.text)# 等同于下面的写法soup.body.get_text()# 还可以这样写,strings是所有文本的生成器for string in soup.body.strings:print(string, end='')
The Dormouse's story Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well. ...
# 获得该标签里的文本。print(soup.title.string)
The Dormouse's story
# Tag对象的get方法可以根据属性的名称获得属性的值,此句表示得到第一个p标签里class属性的值print(soup.p.get('class'))# 和下面的写法等同print(soup.p['class'])
['title']
# 查看a标签的所有属性,以字典形式给出print(soup.a.attrs)
{'href': 'http://example.com/elsie', 'class': ['sister'], 'id': 'link1'}
# 标签的名称soup.title.name
title
soup.find_all('a', id='link1') soup('a', id='link1')
find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
# 传入字符串soup.find_all('p')# 传入正则表达式import re# 必须以b开头for tag in soup.find_all(re.compile("^b")):print(tag.name)# body# b# 含有t就行for tag in soup.find_all(re.compile("t")):print(tag.name)# html# title# 传入列表表示,一次查找多个标签soup.find_all(["a", "b"])# [The Dormouse's story,# Elsie,# Lacie,# Tillie]
<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="sourceCode python"># title不是html的直接子节点,但是会检索其下所有子孙节点soup.html.find_all("title")# [<title>The Dormouse&#39;s story</title>]# 参数设置为False,只会找直接子节点soup.html.find_all("title", recursive=False)# []# title就是head的直接子节点,所以这个参数此时无影响a = soup.head.find_all("title", recursive=False)# [<title name="good">The Dormouse&#39;s story</title>]</pre><div class="contentsignin">登入後複製</div></div>
find_all使用最多的當屬
find_all / find方法了吧,前者查找所有符合條件的數據,傳回一個列表。後者則是這個清單中的第一個資料。
find_all
有一個limit
參數,限制清單的長度(即尋找符合條件的資料的數量)。當limit=1其實就變成了find方法 。
同樣有簡寫方法。
# 查看所有id为link1的p标签soup.find_all('a', id='link1')
上面兩種寫法是等價的,第二種寫法便是簡寫。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="sourceCode python">soup.find_all(&#39;p&#39;, class_=&#39;story&#39;)
soup.find_all(&#39;p&#39;, &#39;story&#39;)
soup.find_all(&#39;p&#39;, attrs={"class": "story"})</pre><div class="contentsignin">登入後複製</div></div>
name
標籤。不僅能填入字串,還能傳入正規表示式、列表、函數、True。
a = soup.find_all(text='Elsie')# 或者,4.4以上版本请使用texta = soup.find_all(string='Elsie')
的話,就沒有限制,什麼都找了。 recursive
呼叫tag的
# 所有div标签soup.select('div')# 所有id为username的元素soup.select('.username')# 所有class为story的元素soup.select('#story')# 所有div元素之内的span元素,中间可以有其他元素soup.select('div span')# 所有div元素之内的span元素,中间没有其他元素soup.select('div > span')# 所有具有一个id属性的input标签,id的值无所谓soup.select('input[id]')# 所有具有一个id属性且值为user的input标签soup.select('input[id="user"]')# 搜索多个,class为link1或者link2的元素都符合soup.select("#link1, #link2")
使用keyword,加上一個或多個限定條件,縮小尋找範圍。
import osimport requestsfrom bs4 import BeautifulSoup# exist_ok=True,若文件夹已经存在也不会报错os.makedirs('xkcd') url = 'https://xkcd.com/'headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/57.0.2987.98 Safari/537.36'}def save_img(img_url, limit=1): r = requests.get(img_url, headers=headers) soup = BeautifulSoup(r.text, 'lxml')try: img = 'https:' + soup.find('div', id='comic').img.get('src')except AttributeError:print('Image Not Found')else:print('Downloading', img) response = requests.get(img, headers=headers)with open(os.path.join('xkcd', os.path.basename(img)), 'wb') as f:for chunk in response.iter_content(chunk_size=1024*1024): f.write(chunk)# 每次下载一张图片,就减1limit -= 1# 找到上一张图片的网址if limit > 0:try: prev = 'https://xkcd.com' + soup.find('a', rel='prev').get('href')except AttributeError:print('Link Not Exist')else: save_img(prev, limit)if __name__ == '__main__': save_img(url, limit=20)print('Done!')
如果按類別查找,由於class關鍵字Python已經使用。可以用class_填入字典。
Downloading Downloading Downloading Downloading Downloading Downloading Downloading Downloading Downloading ... Done!
可以接受字串、正規表示式、函數、True。
搜尋文字值,好像使用string參數也是一樣的結果。
import osimport threadingimport requestsfrom bs4 import BeautifulSoup os.makedirs('xkcd') headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/57.0.2987.98 Safari/537.36'}def download_imgs(start, end):for url_num in range(start, end): img_url = 'https://xkcd.com/' + str(url_num) r = requests.get(img_url, headers=headers) soup = BeautifulSoup(r.text, 'lxml')try: img = 'https:' + soup.find('div', id='comic').img.get('src')except AttributeError:print('Image Not Found')else:print('Downloading', img) response = requests.get(img, headers=headers)with open(os.path.join('xkcd', os.path.basename(img)), 'wb') as f:for chunk in response.iter_content(chunk_size=1024 * 1024): f.write(chunk)if __name__ == '__main__':# 下载从1到30,每个线程下载10个threads = []for i in range(1, 30, 10): thread_obj = threading.Thread(target=download_imgs, args=(i, i + 10)) threads.append(thread_obj) thread_obj.start()# 阻塞,等待线程执行结束都会等待for thread in threads: thread.join()# 所有线程下载完毕,才打印print('Done!')
text參數也可以接受字串、正規表示式、True、清單。
CSS選擇器
from selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport time browser = webdriver.Chrome()# Chrome打开百度首页browser.get('https://www.baidu.com/')# 找到输入区域input_area = browser.find_element_by_id('kw')# 区域内填写内容input_area.send_keys('The Zen of Python')# 找到"百度一下"search = browser.find_element_by_id('su')# 点击search.click()# 或者按下回车# input_area.send_keys('The Zen of Python', Keys.ENTER)time.sleep(3) browser.get('https://www.zhihu.com/') time.sleep(2)# 返回到百度搜索browser.back() time.sleep(2)# 退出浏览器browser.quit()
一個爬蟲小範例上面介紹了requests和beautifulsoup4的基本用法,使用這些已經可以寫一些簡單的爬蟲了。來試試吧。此例子來自《Python程式設計快速上手-讓繁瑣的工作自動化》[美] AI Sweigart
browser.back() # 返回按钮browser.forward() # 前进按钮browser.refresh() # 刷新按钮browser.close() # 关闭当前窗口browser.quit() # 退出浏览器
<div class="code" style="position:relative; padding:0px; margin:0px;"><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="sourceCode python">browser = webdriver.Chrome()
browser.get(&#39;https://passport.csdn.net/account/login&#39;)
browser.find_element_by_id(&#39;username&#39;).send_keys(&#39;haiyu19931121@163.com&#39;)
browser.find_element_by_id(&#39;password&#39;).send_keys(&#39;**********&#39;)
browser.find_element_by_class_name(&#39;logging&#39;).click()</pre><div class="contentsignin">登入後複製</div></div><div class="contentsignin">登入後複製</div></div>
多執行緒下載
單一執行緒的速度有點慢,例如可以使用多執行緒,由於我們在取得prev来看下结果吧。
selenium用来作自动化测试。使用前需要下载驱动,我只下载了Firefox和Chrome的。网上随便一搜就能下载到了。接下来将下载下来的文件其复制到将安装目录下,比如Firefox,将对应的驱动程序放到C:\Program Files (x86)\Mozilla Firefox
,并将这个路径添加到环境变量中,同理Chrome的驱动程序放到C:\Program Files (x86)\Google\Chrome\Application
并将该路径添加到环境变量。最后重启IDE开始使用吧。
下面这个例子会打开Chrome浏览器,访问百度首页,模拟输入The Zen of Python
,随后点击百度一下
,当然也可以用回车代替。Keys
下是一些不能用字符串表示的键,比如方向键、Tab、Enter、Esc、F1~F12、Backspace等。然后等待3秒,页面跳转到知乎首页,接着返回到百度,最后退出(关闭)浏览器。
from selenium import webdriverfrom selenium.webdriver.common.keys import Keysimport time browser = webdriver.Chrome()# Chrome打开百度首页browser.get('https://www.baidu.com/')# 找到输入区域input_area = browser.find_element_by_id('kw')# 区域内填写内容input_area.send_keys('The Zen of Python')# 找到"百度一下"search = browser.find_element_by_id('su')# 点击search.click()# 或者按下回车# input_area.send_keys('The Zen of Python', Keys.ENTER)time.sleep(3) browser.get('https://www.zhihu.com/') time.sleep(2)# 返回到百度搜索browser.back() time.sleep(2)# 退出浏览器browser.quit()
send_keys
模拟输入内容。可以使用element的clear()
方法清空输入。一些其他模拟点击浏览器按钮的方法如下
browser.back() # 返回按钮browser.forward() # 前进按钮browser.refresh() # 刷新按钮browser.close() # 关闭当前窗口browser.quit() # 退出浏览器
以下列举常用的查找Element的方法。
方法名 | 返回的WebElement |
---|---|
find_element_by_id(id) | 匹配id属性值的元素 |
find_element_by_name(name) | 匹配name属性值的元素 |
find_element_by_class_name(name) | 匹配CSS的class值的元素 |
find_element_by_tag_name(tag) | 匹配标签名的元素,如div |
find_element_by_css_selector(selector) | 匹配CSS选择器 |
find_element_by_xpath(xpath) | 匹配xpath |
find_element_by_link_text(text) | 完全匹配提供的text的a标签 |
find_element_by_partial_link_text(text) | 提供的text可以是a标签中文本中的一部分 |
以下代码可以模拟输入账号密码,点击登录。整个过程还是很快的。
以上差不多都是API的罗列,其中有自己的理解,也有照搬官方文档的。
by @sunhaiyu
2017.7.13
以上是對Beautifulsoup和selenium用法的簡單介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!