學習者須要預先掌握Python的數字類型、字符串類型、分支、循環、函數、列表類型、字典類型、文件和第三方庫使用等概念和編程方法。php
Python入門篇:http://www.javashuo.com/article/p-cgbvzsyw-ch.htmlhtml
a. 發送請求html5
使用http庫向目標站點發起請求,即發送一個Request,Request包含:請求頭、請求體等。 python
Request模塊缺陷:不能執行JS 和CSS 代碼。web
b. 獲取響應內容正則表達式
若是requests的內容存在於目標服務器上,那麼服務器會返回請求內容。數據庫
Response包含:html、Json字符串、圖片,視頻等。編程
c. 解析內容json
對用戶而言,就是尋找本身須要的信息。對於Python爬蟲而言,就是利用正則表達式或者其餘庫提取目標信息。瀏覽器
解析html數據:正則表達式(RE模塊),第三方解析庫如Beautifulsoup,pyquery等
解析json數據:json模塊
解析二進制數據:以wb的方式寫入文件
d. 保存數據
解析獲得的數據能夠多種形式,如文本,音頻,視頻保存在本地。
數據庫(MySQL,Mongdb、Redis)
文件
Requests是用python語言基於urllib編寫的,採用的是Apache2 Licensed開源協議的HTTP庫。
安裝:
Win平臺:以「管理員身份運行cmd」,執行 pip install requests
測試:
方法 | 說明 |
requests.request() | 構造一個請求,支撐一下個方法的基礎方法。 |
requests.get() | 獲取HTML網頁的主要方法,對應HTTP的GET |
requests.head() | 獲取HTML網頁投信息的方法,對應HTTP的HEAD |
requests.post() | 向HTML網頁提交POST請求的方法,對應HTTP的POST |
requests.put() | 向HTML網頁提交PUT請求的方法,對應HTTP的PUT |
requests.patch() | 向HTML網頁提交局部修改請求,對應HTTP的PATCH |
requests.delete() | 向HTML網頁提交刪除請求,對應HTTP的DELETE |
帶可選參數的請求方式:
requests.request(method,url,**kwargs)
method:請求方式,對應get/put/post等7種
url:獲取頁面的url連接
**kwargs:控制訪問的參數,均爲可選項,共如下13個
params:字典或字節系列,做爲參數增長到url中
>>> kv = {'key1':'value1','key2':'value2'} >>> r = requests.request('GET','http://python123.io/ws',params=kv) >>> print(r.url)
https://python123.io/ws?key1=value1&key2=value2
data:字典、字節系列或文件對象,做爲requests的內容
>>> kv = {'key1':'value1','key2':'value2'} >>> r = requests.request('POST','http://python123.io/ws',data=kv) >>> body = '主題內容' >>> r = requests.request('POST','http:///python123.io/ws',data=body)
json:JSON格式的數據,做爲equests的內容
>>> kv = {'key1':'value1','key2':'value2'} >>> r = requests.request('POST','http://python123.io/ws',json=kv)
headers:字典,HTTP定製頭
>>> hd = {'user-agent':'Chrome/10'} >>> r = requests.request('POST','http://www.baidu.com',headers=hd)
cookies:字典或cookieJar,Request中的cookie
files:字典類型,傳輸文件
>>> f = {'file':open('/root/po.sh','rb')} >>> r = requests.request('POST','http://python123.io/ws',file=f)
timeout:設置超時時間,秒爲單位。
>>> r = requests.request('GET','http://python123.io/ws',timeout=30)
proxies:字典類型,設置訪問代理服務器,能夠增長登陸驗證。
>>> pxs = {'http':'http://user:pass@10.10.10.2:1234', ... 'https':'https://10.10.10.3:1234'} >>> r = requests.request('GET','http://www.baidu.com',proxies=pxs)
allow_redirects:True/False,默認爲True,重定向開關
stream:True/False,默認爲True,獲取內容當即下載開關
verify:rue/False,默認爲True,認證SSL證書開關
Cert:本地SSL證書路徑
auth:元組類型,支持HTTP認證功能
Response對象包含服務器返回的全部信息,也包含請求的Request信息
注意:編碼爲ISO-8859-1不支持編譯中文,須要設置 r = encoding="utf-8"
Requests庫支持常見的6種鏈接異常
注意:網絡鏈接有風險。異常處理很重要。raise_for_status()若是不等於200則產生異常requests.HTTPError。
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 "產生異常" if __name__ == "__main__": url = "http://www.baidu.com" print(getHTMLText(url))
robots是網站跟爬蟲間的協議,robots.txt(統一小寫)是一種存放於網站根目錄下的ASCII編碼的文本文件,它一般告訴網絡搜索引擎的漫遊器(又稱網絡蜘蛛),此網站中的哪些內容是不該被搜索引擎的漫遊器獲取的,哪些是能夠被漫遊器獲取的。由於一些系統中的URL是大小寫敏感的,因此robots.txt的文件名應統一爲小寫。robots.txt應放置於網站的根目錄下。
網絡爬蟲的尺寸:
a. 網絡爬蟲的「性能」騷擾
web默認接受人類訪問,因爲網絡爬蟲的頻繁訪問會給服務器帶來巨大的額資源開銷。
b. 網絡爬蟲的法律風險
服務器上的數據有產權歸屬,網絡爬蟲獲取數據牟利將帶來法律風險
c. 網絡爬蟲的隱私泄露
網絡爬蟲可能具有突破簡單控制訪問的能力,獲取被保護的數據從而泄露我的隱私。
a. 來源審查:判斷User-Agent進行限制
檢查來訪HTTP協議頭的user-agent域,只響應瀏覽器或友好爬蟲的訪問
b. 發佈公告:Robots協議
告知全部爬蟲網站的爬取策略,要求遵照Robots協議
京東的Robots協議:
https://www.jd.com/robots.txt
#註釋,*表明全部,/表明根目錄
對robots協議的理解
自動或人工識別roboes.txt,z再進行內容爬取。
robots協議是建議但非約束性,網絡爬蟲能夠補遵照,但存在法律風險。
原則:人類行爲能夠補參考robots協議,好比正常閱覽網站,或者較少爬取網站頻率。
目標頁面地址:https://item.jd.com/5089267.html
實例代碼:
import requests url = 'https://item.jd.com/5089267.html' try: r = requests.get(url) r.raise_for_status() r.encoding =r.apparent_encoding print(r.text[:1000]) except: print("爬取失敗")
結果:
目標頁面地址:http://product.dangdang.com/26487763.html
代碼:
import requests url = 'http://product.dangdang.com/26487763.html' try: r = requests.get(url) r.raise_for_status() r.encoding =r.apparent_encoding print(r.text[:1000]) except IOError as e: print(str(e))
出現報錯:
HTTPConnectionPool(host='127.0.0.1', port=80): Max retries exceeded with url: /26487763.html (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x10fc390>: Failed to establish a new connection: [Errno 111] Connection refused',))
報錯緣由:噹噹網拒毫不合理的瀏覽器訪問。
查看初識的http請求頭:
print(r.request.headers)
代碼改進:構造合理的HTTP請求頭
import requests url = 'http://product.dangdang.com/26487763.html' try: kv = {'user-agent':'Mozilla/5.0'} r = requests.get(url,headers=kv) r.raise_for_status() r.encoding =r.apparent_encoding print(r.text[:1000]) except IOError as e: print(str(e))
結果正常爬取:
百度關鍵詞接口:http://www.baidu.com/s?wd=keyword
代碼實現:
import requests keyword = "python" try: kv = {'wd':keyword} r = requests.get("http://www.baidu.com/s",params=kv) print(r.request.url) r.raise_for_status() print(len(r.text)) except IOError as e: print(str(e))
執行結果:
360關鍵詞接口:
http://www.so.com/s?q=keyword
代碼實現:
import requests keyword = "Linux" try: kv = {'q':keyword} r = requests.get("http://www.so.com/s",params=kv) print(r.request.url) r.raise_for_status() print(len(r.text)) except IOError as e: print(str(e))
執行結果:
網絡圖片連接的格式:
http://FQDN/picture.jpg
校花網:http://www.xiaohuar.com
選擇一個圖片地址:http://www.xiaohuar.com/d/file/20141116030511162.jpg
實現代碼:
import requests import os url = "http://www.xiaohuar.com/d/file/20141116030511162.jpg" dir = "D://pics//" path = dir + url.split('/')[-1] #設置圖片保存路徑並以原圖名名字命名 try: if not os.path.exists(dir): os.mkdir(dir) if not os.path.exists(path): r = requests.get(url) with open(path,'wb') as f: f.write(r.content) f.close() print("文件保存成功") else: print("文件已存在") except IOError as e: print(str(e))
查看圖片已經存在:
5.5 ip地址歸屬地查詢
ip地址歸屬地查詢網站接口:http://www.ip138.com/ips138.asp?ip=
實現代碼:
import requests url = "http://www.ip38.com/ip.php?ip=" try: r = requests.get(url+'104.193.88.77') r.raise_for_status() r.encoding = r.apparent_encoding print(r.text) except IOError as e: print(str(e))
打開有道翻譯,在開發者模式依次單擊「Network」按鈕和「XHR」按鈕,找到翻譯數據:
import requests import json def get_translate_date(word=None): url = "http://fanyi.youdao.com/translate?smartresult=dict&smartresult=rule" #post參數須要放在請求實體裏,構建一個新字典 form_data = {'i': word, 'from': 'AUTO', 'to': 'AUTO', 'smartresult': 'dict', 'client': 'fanyideskweb', 'salt': '15569272902260', 'sign': 'b2781ea3e179798436b2afb674ebd223', 'ts': '1556927290226', 'bv': '94d71a52069585850d26a662e1bcef22', 'doctype': 'json', 'version': '2.1', 'keyfrom': 'fanyi.web', 'action': 'FY_BY_REALTlME' } #請求表單數據 response = requests.post(url,data=form_data) #將JSON格式字符串轉字典 content = json.loads(response.text) #打印翻譯後的數據 print(content['translateResult'][0][0]['tgt']) if __name__ == '__main__': word = input("請輸入你要翻譯的文字:") get_translate_date(word)
執行結果:
from bs4 import BeautifulSoup soup = BeautifulSoup('<p>Hello</p>','lxml') print(soup.p.string)
>>> from bs4 import BeautifulSoup >>> import requests >>> r = requests.get("http://python123.io/ws/demo.html") >>> demo = r.text >>> demo '<html><head><title>This is a python demo page</title></head>\r\n<body>\r\n<p class="title"><b>The demo python introduces several python courses.</b></p>\r\n<p class="course">Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\r\n<a href="http://www.icourse163.org/course/BIT-268001" class="py1" id="link1">Basic Python</a> and <a href="http://www.icourse163.org/course/BIT-1001870001" class="py2" id="link2">Advanced Python</a>.</p>\r\n</body></html>' >>> soup = BeautifulSoup(demo,"html.parser") >>> soup.title #獲取標題 <title>This is a python demo page</title> >>> soup.a #獲取a標籤 <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">Basic Python</a> >>> soup.title.string 'This is a python demo page' >>> soup.prettify() #輸出html標準格式內容 '<html>\n <head>\n <title>\n This is a python demo page\n </title>\n </head>\n <body>\n <p class="title">\n <b>\n The demo python introduces several python courses.\n </b>\n </p>\n <p class="course">\n Python is a wonderful general-purpose programming language. You can learn Python from novice to professional by tracking the following courses:\n <a class="py1" href="http://www.icourse163.org/course/BIT-268001" id="link1">\n Basic Python\n </a>\n and\n <a class="py2" href="http://www.icourse163.org/course/BIT-1001870001" id="link2">\n Advanced Python\n </a>\n .\n </p>\n </body>\n</html>' >>> soup.a.name #每一個<tag>都有本身的名字,經過<tag>.name獲取 'a' >>> soup.p.name 'p' >>> tag = soup.a >>> tag.attrs {'href': 'http://www.icourse163.org/course/BIT-268001', 'class': ['py1'], 'id': 'link1'} >>> tag.attrs['class'] ['py1'] >>> tag.attrs['href'] 'http://www.icourse163.org/course/BIT-268001' >>> type(tag.attrs) <class 'dict'> >>> type(tag) <class 'bs4.element.Tag'> >>>
from bs4 import BeautifulSoup import requests demo = requests.get("http://python123.io/ws/demo.html").text soup = BeautifulSoup(demo,"html.parser") #標籤樹的上行遍歷 print("遍歷兒子節點:\n") for child in soup.body.children: print(child) print("遍歷子孫節點:\n") for child1 in soup.body.descendants: print(child1) print(soup.title.parent) print(soup.html.parent) for parent in soup.a.parents: if parent is None: print(parent) else: print(parent.name) #標籤樹的平行遍歷 print(soup.a.next_sibling) print(soup.a.next_sibling.next_sibling) print(soup.a.previous_sibling)
正則表達式是處理字符串的強大工具,它有本身特定的語法結構,實現字符串的檢索、替換、匹配驗證均可以。對於爬蟲來講,
從HTML裏提取想要的信息很是方便。python的re庫提供了整個正則表達式的實現
這裏介紹一個正則表達式測試工具http://tool.oschina.net/regex,輸入待匹配的文本,然選擇經常使用的正則表達式,獲得相應的匹配結果,
適合新手入門。這裏輸入:
hello,my phone is 18898566588 and email is david@gmail.com, and wen is https://www.cnblogs.com/wenwei-blog/
點擊「匹配Email地址」,便可匹配出網址。
方法/屬性
|
做用
|
re.match(pattern, string, flags=0)
|
從字符串的起始位置匹配,若是起始位置匹配不成功的話,match()就返回none
|
re.search(pattern, string, flags=0)
|
掃描整個字符串並返回第一個成功的匹配
|
re.findall(pattern, string, flags=0)
|
找到RE匹配的全部字符串,並把他們做爲一個列表返回
|
re.finditer(pattern, string, flags=0)
|
找到RE匹配的全部字符串,並把他們做爲一個迭代器返回
|
re.sub(pattern, repl, string, count=0, flags=0)
|
替換匹配到的字符串
|
#!/usr/bin/python3 import re #替換 phone = '18898537584 #這是個人電話號碼' print('個人電話號碼:',re.sub('#.*','',phone)) #去掉註釋 print(re.sub('\D','',phone)) #search ip_addr = re.search('(\d{3}\.){1,3}\d{1,3}\.\d{1,3}',os.popen('ifconfig').read()) print(ip_addr) #match >>> a = re.match('\d+','2ewrer666dad3123df45') >>> print(a.group()) 2
>>> import re >>> print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(0)) #返回總體 34324-d >>> print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(1)) #返回第一組 34324 >>> print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(2)) #獲取第二組 d >>> print(re.search(r'(\d+)-([a-z])','34324-dfsdfs777-hhh').group(3)) #不存在。報錯「no such group」 Traceback (most recent call last): File "<stdin>", line 1, in <module> IndexError: no such group
compile 函數用於編譯正則表達式,生成一個正則表達式( Pattern )對象。語法格式:
re.compile(pattern[, flags])
參數:
pattern : 一個字符串形式的正則表達式
flags : 可選,表示匹配模式,好比忽略大小寫,多行模式等,具體參數爲:
re.I 忽略大小寫
re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依賴於當前環境
re.M 多行模式
re.S 即爲 . 而且包括換行符在內的任意字符(. 不包括換行符)
re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依賴於 Unicode 字符屬性數據庫
re.X 爲了增長可讀性,忽略空格和 # 後面的註釋
經常使用的是re.I和re.S
>>> import re >>> pattern = re.compile('\d+',re.S) #用於匹配至少一個數字 >>> res = re.findall(pattern,"my phone is 18898566588") >>> print(res) ['18898566588']
代碼整合
import json import requests from requests.exceptions import RequestException #引入異常 import re import time def get_one_page(url): try: response = requests.get(url) if response.status_code == 200: #由狀態碼判斷返回結果 return response.text #返回網頁內容 return None except RequestException: return None def parse_one_page(html): pattern = re.compile('<dd>.*?board-index.*?>(\d+)</i>.*?data-src="(.*?)".*?name"><a' + '.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>' + '.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>', re.S) #compile函數表示一個方法對象,re.s匹配多行 items = re.findall(pattern, html) #以列表形式返回所有能匹配的字符串。 for item in items: #將結果以字典形式返回鍵值對 yield { #把這個方法變成一個生成器 'index': item[0], 'image': item[1], 'title': item[2], 'actor': item[3].strip()[3:], 'time': item[4].strip()[5:], 'score': item[5] + item[6] #將評分整數和小數結合起來 } def write_to_file(content): with open('result.txt', 'a', encoding='utf-8') as f: #將結果寫入文件 f.write(json.dumps(content, ensure_ascii=False) + '\n') def main(offset): url = 'http://maoyan.com/board/4?offset=' + str(offset) html = get_one_page(url) for item in parse_one_page(html): print(item) write_to_file(item) if __name__ == '__main__': for i in range(10): main(offset=i * 10) time.sleep(1)
Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。 其能夠應用在數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是爲了頁面抓取 (更確切來講, 網絡抓取 )所設計的, 也能夠應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。Scrapy 使用了 Twisted異步網絡庫來處理網絡通信。總體架構大體以下
Scrapy主要包括瞭如下組件:
Scrapy運行流程大概以下:
scrapy經常使用命令
scrapy startproject <爬蟲名稱> 建立爬蟲名稱(惟一)
scrapy genspider<爬蟲項目名稱> 建立爬蟲項目名稱
scrapy list 列出全部爬蟲名稱
scrapy crawl <爬蟲名稱> 運行爬蟲
爬取目標:電影排名、電影名稱、電影評分、電影評論數
建立爬蟲項目和爬蟲
scrapy startproject DoubanMovieTop
cd DoubanMovieTop
scrapy genspider douban
修改默認「user-agent」和reboots爲True
修改settings.py文件如下參數:
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36'
ROBOTSTXT_OBEY = False
Item使用簡單的class定義語法以及Field對象來聲明。
寫入下列代碼聲明Item
import scrapy class DoubanmovietopItem(scrapy.Item): # define the fields for your item here like: # name = scrapy.Field() #排名 ranking = scrapy.Field() #電影名稱 movie_name = scrapy.Field() #評分 score = scrapy.Field() #評論人數 score_num = scrapy.Field()
分析網頁源碼抓取所需信息
# -*- coding: utf-8 -*- import scrapy from DoubanMovieTop.items import DoubanmovietopItem class DoubanSpider(scrapy.Spider): name = 'douban' #allowed_domains = ['movie.douban.com'] def start_requests(self): start_urls = 'https://movie.douban.com/top250' yield scrapy.Request(start_urls) def parse(self, response): item = DoubanmovietopItem() movies = response.xpath('//ol[@class="grid_view"]/li') for movie in movies: item['ranking'] = movie.xpath('.//div[@class="pic"]/em/text()').extract()[0] item['movie_name'] = movie.xpath('.//div[@class="hd"]/a/span[1]/text()').extract()[0] item['score'] = movie.xpath('.//div[@class="star"]/span[@class="rating_num"]/text()').extract()[0] item['score_num'] = movie.xpath('.//div[@class="star"]/span/text()').re(r'(\d+)人評價')[0] #Selector也有一種.re() yield item next_url = response.xpath('//span[@class="next"]/a/@href').extract() if next_url: next_url = 'https://movie.douban.com/top250' + next_url[0] yield scrapy.Request(next_url)
運行爬蟲寫入文件中
scrapy crawl douban -o douban.csv
使用wps打開excel表格查看抓取結果