網絡爬蟲(又被稱爲網頁蜘蛛,網絡機器人,在FOAF社區中間,更常常的稱爲網頁追逐者),是一種按照必定的規則,自動地抓取萬維網信息的程序或者腳本。另一些不常使用的名字還有螞蟻、自動索引、模擬程序或者蠕蟲。html
Python標準庫中提供了:urllib、urllib二、httplib等模塊以供Http請求,可是,它的 API 太渣了。它是爲另外一個時代、另外一個互聯網所建立的。它須要巨量的工做,甚至包括各類方法覆蓋,來完成最簡單的任務。python
Requests 是使用 Apache2 Licensed 許可證的 基於Python開發的HTTP 庫,其在Python內置模塊的基礎上進行了高度的封裝,從而使得Pythoner進行網絡請求時,變得美好了許多,使用Requests能夠垂手可得的完成瀏覽器可有的任何操做。git
一、GET請求github
# 一、無參數實例 import requests ret = requests.get('https://github.com/timeline.json') print(ret.url) print(ret.text) # 二、有參數實例 import requests payload = {'key1': 'value1', 'key2': 'value2'} ret = requests.get("http://httpbin.org/get", params=payload) print(ret.url) print(ret.text)
二、POST請求正則表達式
# 一、基本POST實例 import requests payload = {'key1': 'value1', 'key2': 'value2'} ret = requests.post("http://httpbin.org/post", data=payload) print(ret.text) # 二、發送請求頭和數據實例 import requests import json url = 'https://api.github.com/some/endpoint' payload = {'some': 'data'} headers = {'content-type': 'application/json'} ret = requests.post(url, data=json.dumps(payload), headers=headers) print(ret.text) print(ret.cookies)
三、其餘請求數據庫
requests.get(url, params=None, **kwargs) requests.post(url, data=None, json=None, **kwargs) requests.put(url, data=None, **kwargs) requests.head(url, **kwargs) requests.delete(url, **kwargs) requests.patch(url, data=None, **kwargs) requests.options(url, **kwargs) # 以上方法均是在此方法的基礎上構建 requests.request(method, url, **kwargs)
四、響應內容json
Requests 會自動解碼來自服務器的內容。大多數 unicode 字符集都能被無縫地解碼。windows
請求發出後,Requests 會基於 HTTP 頭部對響應的編碼做出有根據的推測。api
當你訪問 r.text
之時,Requests 會使用其推測的文本編碼。你能夠找出 Requests 使用了什麼編碼,而且可以使用 r.encoding
屬性來改變它:瀏覽器
response.encoding = 'ISO-8859-1'
以字節的方式訪問請求響應體
response.content
五、Cookie
設置cookies:
requests.get('http://www.google.com/', cookies={'key1': 'value1', 'key2': 'value2'})
若是使用Session的話,能夠這樣設置
session = requests.session() cookies_dict = {'key1': 'value1', 'key2': 'value2'} session.cookies = requests.utils.cookiejar_from_dict(cookies)
保存Cookies:
cookies_dict = requests.utils.dict_from_cookiejar(session.cookies)
普通方式:
import requests response = requests.get('http://www.baidu.com') cookies = response.cookies # 字典形式存儲 cookies_dict = response.cookies.get_dict()
另外提供一個Chrome瀏覽器中的Cookies字符串轉字典的函數
def cookie_dict_form_text(cookie_text): cookies = {} for cookie_unit_text in cookie_text.split('; '): cookie_unit = cookie_unit_text.split('=') if len(cookie_unit) >= 2: cookies[cookie_unit[0]] = cookie_unit[1] return cookies
六、會話對象Session
會話對象讓你可以跨請求保持某些參數。它也會在同一個 Session 實例發出的全部請求之間保持 cookie,
conn = requests.session() r = conn.get('http://dig.chouti.com/') print(r.request.headers) r = conn.get('http://dig.chouti.com/') print(r.request.headers) #{'User-Agent': 'python-requests/2.12.4', 'Connection': 'keep-alive', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate'} #{'User-Agent': 'python-requests/2.12.4', 'Connection': 'keep-alive', 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate', 'Cookie': 'gpsd=6392a33cd7960ef59be44158c19f58f8; route=e7bfb38cb3b5a04758a9513df6738c7e; JSESSIONID=aaaZZDnAXDsv6eA-7rHTv'}
在第二次訪問時,已經自動添加上了cookies
七、py2版本下亂碼問題
response.content.decode("utf8","ignore").encode("gbk","ignore") 就不會有亂碼了
這裏所使用的ignore屬性意思是忽略其中有異常的編碼,僅顯示有效的編碼。py2下亂碼問題能夠參考 👉點我
八、使用代理proxies
import requests proxies = { "http": "http://10.10.1.10:3128", "https": "http://10.10.1.10:1080", } requests.get("http://example.org", proxies=proxies)
這裏的能夠經過ip測試網站進行驗證是否成功使用了代理 http://ip.chinaz.com/(查看你訪問後的網站的您的IP是多少來判斷)
九、其餘
requests模塊已經將經常使用的Http請求方法爲用戶封裝完成,用戶直接調用其提供的相應方法便可,其中方法的全部參數有:
def request(method, url, **kwargs): """Constructs and sends a :class:`Request <Request>`. :param method: method for the new :class:`Request` object. :param url: URL for the new :class:`Request` object. :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param json: (optional) json data to send in the body of the :class:`Request`. :param headers: (optional) Dictionary of HTTP Headers to send with the :class:`Request`. :param cookies: (optional) Dict or CookieJar object to send with the :class:`Request`. :param files: (optional) Dictionary of ``'name': file-like-objects`` (or ``{'name': ('filename', fileobj)}``) for multipart encoding upload. :param auth: (optional) Auth tuple to enable Basic/Digest/Custom HTTP Auth. :param timeout: (optional) How long to wait for the server to send data before giving up, as a float, or a :ref:`(connect timeout, read timeout) <timeouts>` tuple. :type timeout: float or tuple :param allow_redirects: (optional) Boolean. Set to True if POST/PUT/DELETE redirect following is allowed. :type allow_redirects: bool :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. :param verify: (optional) whether the SSL cert will be verified. A CA_BUNDLE path can also be provided. Defaults to ``True``. :param stream: (optional) if ``False``, the response content will be immediately downloaded. :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. :return: :class:`Response <Response>` object :rtype: requests.Response Usage:: >>> import requests >>> req = requests.request('GET', 'http://httpbin.org/get') <Response [200]> """ # By using the 'with' statement we are sure the session is closed, thus we # avoid leaving sockets open which can trigger a ResourceWarning in some # cases, and look like a memory leak in others. with sessions.Session() as session: return session.request(method=method, url=url, **kwargs)
更多requests模塊相關的文檔見:http://cn.python-requests.org/zh_CN/latest/
Scrapy是一個爲了爬取網站數據,提取結構性數據而編寫的應用框架。 其能夠應用在數據挖掘,信息處理或存儲歷史數據等一系列的程序中。
其最初是爲了頁面抓取 (更確切來講, 網絡抓取 )所設計的, 也能夠應用在獲取API所返回的數據(例如 Amazon Associates Web Services ) 或者通用的網絡爬蟲。Scrapy用途普遍,能夠用於數據挖掘、監測和自動化測試。
Scrapy 使用了 Twisted異步網絡庫來處理網絡通信。總體架構大體以下
Scrapy主要包括瞭如下組件:
Scrapy運行流程大概以下:
引擎從調度器中取出一個連接(URL)用於接下來的抓取
引擎把URL封裝成一個請求(Request)傳給下載器
下載器把資源下載下來,並封裝成應答包(Response)
爬蟲解析Response
解析出實體(Item),則交給實體管道進行進一步的處理
解析出的是連接(URL),則把URL交給調度器等待抓取
1、安裝
1.安裝 setuptools
setuptools 官網下載
python ez_setup.py
2.安裝 pip
下載pip源碼 官網下載,安裝
python setup.py install
3.安裝 scrapy
pip install requests pip install scrapy
windows下
安裝依賴scrapy的組件
http://www.lfd.uci.edu/~gohlke/pythonlibs/,Ctrl+F搜索 lxml、Twisted、Scrapy,下載對應的版本,
例如:lxml-3.7.3-cp35-cp35m-win_adm64.whl,表示lxml的版本爲3.7.3,對應的python版本爲3.5-64bit。
a.pip3 install wheel
b.pip3 install lxml-3.7.3-cp35-cp35m-win_amd64.whl
c.pip3 install Twisted-17.1.0-cp35-cp35m-win_amd64.whl
d.pip3 install Scrapy-1.3.2-py2.py3-none-any.whl
Srapy已經安裝成功,還要下載pywin32,找到對應版本下載,一路下一步安裝便可。安裝完成後,就能夠正常使用Scrapy了。
URL:https://sourceforge.net/projects/pywin32/files/pywin32/Build%20220/
2、基本使用
一、建立項目
scrapy startproject your_project_name
自動建立目錄:
project_name/ scrapy.cfg project_name/ __init__.py items.py pipelines.py settings.py spiders/ __init__.py
文件說明:
scrapy.cfg 項目的配置信息,主要爲Scrapy命令行工具提供一個基礎的配置信息。(真正爬蟲相關的配置信息在settings.py文件中)
items.py 設置數據存儲模板,用於結構化數據,如:Django的Model
pipelines 數據處理行爲,如:通常結構化的數據持久化
settings.py 配置文件,如:遞歸的層數、併發數,延遲下載等
spiders 爬蟲目錄,如:建立文件,編寫爬蟲規則
注意:通常建立爬蟲文件時,以網站域名命名
二、基本選擇器Selector
一個/ 表示根目錄 //div[@class='item_list']/ //div[@class='item_list']/div 孩子 //div[@class='item_list']//div 子子孫孫 # 多屬性匹配 /div[@class='item_list'][@id='a1']/ # 獲取內容 //div[@class='item_list']//div/text() # 獲取屬性 //div[@class='item_list']//img/@src //a/@href # 索引方式 //div[@class='item_list']/div[1] 默認從第1個開始 //div[@class='item_list']/div[2]
正則表達式使用:
<body> <li class="item-"><a href="link.html">first item</a></li> <li class="item-0"><a href="link1.html">first item</a></li> <li class="item-1"><a href="link2.html">second item</a></li> </body> 類型一: ret = Selector(response=response).xpath('//li[re:test(@class, "item-\d*")]//@href').extract() 類型二: ret = Selector(response=response).xpath('//.select('div//a[1]').re('xx:(\w+)') # 不用寫.extract()
更多選擇器規則:http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/selectors.html
三、編寫爬蟲
import scrapy class XiaoHuarSpider(scrapy.spiders.Spider): name = "xiaohuar" # spider_name allowed_domains = ["xiaohuar.com"] start_urls = [ "http://www.xiaohuar.com/hua/", ] def parse(self, response): print(response, type(response)) current_url = response.url body = response.body unicode_body = response.body_as_unicode() print(unicode_body )
四、運行
進入project_name目錄,運行命令
scrapy crawl spider_name --nolog
五、兩種定義查找的方式
from scrapy.selector import Selector ret = Selector(response=response).xpath('//li[re:test(@class, "item-\d*")]//@href').extract() from scrapy.selector import HtmlXPathSelector hxs = HtmlXPathSelector(response) items = hxs.select('//div[@class="item_list infinite_scroll"]/div').extract()
五、去除重複url
通常採用md5 加密url 存放到set() 或數據庫裏
import hashlib url_set = set() md5_obj = hashlib.md5() md5_obj.update(response.url) md5_url = md5_obj.hexdigest() if md5_url in url_set : pass else: pass
六、遞歸的訪問
以上的爬蟲僅僅是爬去初始頁,而咱們爬蟲是須要源源不斷的執行下去,直到全部的網頁被執行完畢
#!/usr/bin/env python # -*-coding:utf-8 -*- from scrapy.selector import Selector from scrapy.http import Request from scrapy.selector import HtmlXPathSelector import scrapy import os import requests import hashlib class XiaoHuarSpider(scrapy.spiders.Spider): name = "xiaohuar" url_set = set() allowed_domains = ["xiaohuar.com"] start_urls = [ "http://www.xiaohuar.com/hua/", ] def parse(self, response): # 分析頁面 # 找到頁面中符合規則的內容(校花圖片),保存 # 找到全部的a標籤,再訪問其餘a標籤,一層一層的搞下去 md5_obj = hashlib.md5() md5_obj.update(bytes(response.url,encoding='utf-8')) md5_url = md5_obj.hexdigest() if md5_url in XiaoHuarSpider.url_set: pass else: hxs = HtmlXPathSelector(response) items = hxs.select('//div[@class="item_list infinite_scroll"]/div') #對象 for i in range(len(items)): src = hxs.select( '//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/a/img/@src' % i).extract() name = hxs.select( '//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/span/text()' % i).extract() school = hxs.select( '//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/div[@class="btns"]/a/text()' % i).extract() print(name,school,src) if src and name and school: ab_src = "http://www.xiaohuar.com" + src[0] file_name = "%s_%s.jpg" % (school[0],name[0]) file_path = os.path.join("E:/image", file_name) f = open(file_path, 'wb') f.write(requests.get(ab_src).content) f.close() current_page_urls = hxs.select('//a/@href').extract() for i in range(len(current_page_urls)): url = current_page_urls[i] if url.startswith('http://www.xiaohuar.com/list-1-'): url_ab = url yield Request(url_ab, callback=self.parse)
以上代碼將符合規則的頁面中的圖片保存在指定目錄,而且在HTML源碼中找到全部的其餘 a 標籤的href屬性,從而「遞歸」的執行下去,直到全部的頁面都被訪問過爲止。以上代碼之因此能夠進行「遞歸」的訪問相關URL,關鍵在於parse方法使用了 yield Request對象。
注:能夠修改settings.py 中的配置文件,以此來指定「遞歸」的層數,如: DEPTH_LIMIT = 1
七、格式化處理
上述實例只是簡單的圖片處理,因此在parse方法中直接處理。若是對於想要獲取更多的數據(獲取頁面的價格、商品名稱、QQ等),則能夠利用Scrapy的items將數據格式化,而後統一交由pipelines來處理。
在items.py中建立類:
import scrapy class Scrapyapp1Item(scrapy.Item): src = scrapy.Field() name = scrapy.Field() school = scrapy.Field()
上述定義模板,之後對於從請求的源碼中獲取的數據贊成按照此結構來獲取,因此在spider中須要有一下操做:
#!/usr/bin/env python # -*-coding:utf-8 -*- from scrapy.selector import Selector from scrapy.http import Request from scrapy.selector import HtmlXPathSelector import scrapy import hashlib class XiaoHuarSpider(scrapy.spiders.Spider): name = "xiaohuar" url_set = set() allowed_domains = ["xiaohuar.com"] start_urls = [ "http://www.xiaohuar.com/hua/", ] def parse(self, response): # 分析頁面 # 找到頁面中符合規則的內容(校花圖片),保存 # 找到全部的a標籤,再訪問其餘a標籤,一層一層的搞下去 md5_obj = hashlib.md5() md5_obj.update(bytes(response.url,encoding='utf-8')) md5_url = md5_obj.hexdigest() if md5_url in XiaoHuarSpider.url_set: pass else: hxs = HtmlXPathSelector(response) items = hxs.select('//div[@class="item_list infinite_scroll"]/div') #對象 for i in range(len(items)): src = hxs.select( '//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/a/img/@src' % i).extract() name = hxs.select( '//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/span/text()' % i).extract() school = hxs.select( '//div[@class="item_list infinite_scroll"]/div[%d]//div[@class="img"]/div[@class="btns"]/a/text()' % i).extract() # print(name,school,src) if src and name and school: from scrapyapp1 import items obj = items.Scrapyapp1Item() obj['src'] = src[0] obj['name'] = name[0] obj['school'] = school[0] yield obj
此處代碼的關鍵在於:
將獲取的數據封裝在了Item對象中
yield Item對象 (一旦parse中執行yield Item對象,則自動將該對象交個pipelines的類來處理)
import os import requests class Scrapyapp1Pipeline(object): def process_item(self, item, spider): print(item['name'],item['school'],item['src']) ab_src = "http://www.xiaohuar.com" + item['src'] file_name = "%s_%s.jpg" % (item['school'],item['name']) file_path = os.path.join("E:/image", file_name) f = open(file_path, 'wb') f.write(requests.get(ab_src).content) f.close() return item
若是有多個pipelines類,到底Scapy會自動執行那個?哈哈哈哈,固然須要先配置了,否則Scapy就蒙逼了。。。
在settings.py中作以下配置:
ITEM_PIPELINES = { 'scrapyapp1.pipelines.Scrapyapp1Pipeline': 100, ...., } # 每行後面的整型值,肯定了他們運行的順序,item按數字從低到高的順序,經過pipeline,一般將這些數字定義在0-1000範圍內。