1、爬取內容顯示亂碼
1、原因:比如網頁編碼是gbk編碼的,但是我們用了錯誤的方式比如utf-8解碼,因而出現亂碼 2、基礎知識: (1)python3.6 默認編碼為Unicode;正常的字符串就是Unicode (2)計算機中存儲的信息都是二進制的 (3)編碼decode:真實字符→二進制 (4)解碼encode:二進制→真實字符 (5)一般來說在Unicode2個字節的,在UTF8需要3個字節;但對於大多數語言來說,只需要1個字節就能編碼,如果采用Unicode會極大浪費,於是出現了變長的編碼格式UTF8 (6)GB2312的出現,基本滿足了漢字的計算機處理需要,但對於人名、古漢語等方面出現的罕用字,GB2312不能處理,這導致了后來GBK及GB18030漢字字符集的出現。 3、各種編碼方式: (1)ASCII :1字節8個bit位表示一個字符的編碼格式,最多可以給256個字(包括字母、數字、標點符號、控制字符及其他符號)分配(或指定)數值。 (2)ISO8859-1 :1字節8個bit位表示一個字符的編碼格式,僅支持英文字符、數字及常見的符號,3.6%的全球網站使用這個編碼。 (3)GB2312:2字節16個bit位表示一個字符的編碼格式,基本滿足了漢字的計算機處理需要 (4)GBK:2字節16個bit位表示一個字符的編碼格式,GBK即漢字內碼擴展規范 (5)Unicode :2字節16個bit位表示一個字符的編碼格式,基本能把全球所有字符表示完, (6)UTF8:變長字節編碼格式,英文1字節,漢字3字節,較復雜的更高字節編碼, 4、實例: s = "好" #默認Unicode編碼 print(s.encode("gbk")) #Unicode轉gbk #輸出2字節: b'\xba\xc3' print(s.encode("utf-8")) #Unicode轉utf-8 #輸出3字節:b'\xe5\xa5\xbd' print(s.encode("gb2312")) #Unicode轉gb2312 #輸出2字節:b'\xba\xc3' print(s.encode("gb2312").decode("gb2312")) #Unicode解碼為gb2312再編碼為Unicode print(s.encode("utf8").decode("utf8")) #Unicode解碼為utf8再編碼為Unicode #輸出:好
(2)解決方法
方法: 查看網頁是什么編碼,並設置該編碼格式,或者包含大於這個的編碼,如gb2312編碼的網頁可以設置gbk的編碼方式。 代碼: solution1:response.encoding = response.apparent_encoding solution2 :response.encoding = 'utf-8' response.encoding = 'gbk'
2、pymongo.errors.CursorNotFound:
(1)原因:
默認 mongo server維護連接的時間窗口是十分鍾;默認單次從server獲取數據是101條或者 大於1M小於16M的數據;所以默認情況下,如果10分鍾內未能處理完數據,則拋出該異常。
(2)解決方法:
方法: no_cursor_timeout=True:設置連接永遠不超時 batch_size:估計每批次獲取數據量的條數;讓MongoDB客戶端每次抓取的文檔在10分鍾內能用完 代碼: import pymongo client = pymongo.MongoClient(host='localhost', port=27017) db = client.test collection = db.testtable cursor = collection.find(no_cursor_timeout=True, batch_size=5)
3、TypeError: can’t pickle _thread.lock objects和EOFError: Ran out of input
(1)原因:
1、進程池內部處理使用了pickle模塊(用於python特有的類型和python的數據類型間進行轉換)中的dump(obj, file, protocol=None,)方法對參數進行了封裝處理. 2、在參數傳遞中如果自定義了數據庫存儲類mongo或者redis等數據庫,會造成進程池內部處理封裝過程無法對其進行處理. 3、錯誤代碼產生異常的實例1: import multiprocessing import pymongo class Test: def __init__(self, collection): self.collection = collection def savedata(self): self.collection.insert_one({'key': 'value'}) def main(): client = pymongo.MongoClient(host='localhost', port=27017) db = client.test collecttable = db.testtable test = Test(collecttable) p1 = multiprocessing.Process(target=test.savedata) p1.start() p1.join() print('進程已結束') if __name__ == '__main__': main() 4、錯誤代碼產生異常的實例2: import multiprocessing import pymongo class Test: def __init__(self): pass def savedata(self, collecttable): collecttable.insert_one({'key': 'value'}) def main(): client = pymongo.MongoClient(host='localhost', port=27017) db = client.test collecttable = db.testtable test = Test() p1 = multiprocessing.Process(target=test.savedata, args=(collecttable,)) p1.start() p1.join() print('進程已結束') if __name__ == '__main__': main()
(2)解決方法:
方法: 在參數傳遞時,不能將數據庫集合作為類的參數進行傳遞,只能在函數里面創建使用數據庫 代碼: import multiprocessing import pymongo class Test: def __init__(self): pass def savedata(self): client = pymongo.MongoClient(host='localhost', port=27017) db = client.test collecttable = db.testtable collecttable.insert_one({'key': 'value'}) def main(): test = Test() p1 = multiprocessing.Process(target=test.savedata) p1.start() p1.join() print('進程已結束') if __name__ == '__main__': main()
4、redis.exceptions.DataError: Invalid input of type: ‘dict’. Convert to a byte, string or number first.
(1)原因:
1、redis存入數據類型錯誤,應該是字節或者是字符串或者是數字類型 2、錯誤實例: from redis import StrictRedis dict = {'key': 'value'} r = StrictRedis(host='localhost', port=6379) r.rpush('test', dict)
(2)解決方法:
方法: 使用json模塊,json.dumps(dict)可以將字典類型的轉換為字符串 代碼: import json from redis import StrictRedis dict = {'key': 'value'} r = StrictRedis(host='localhost', port=6379) data = json.dumps(dict) r.rpush('test', data) print(r.lpop('test')) #輸出: b'{"key": "value"}'
5、json.dumps()中文未正確顯示
(1)原因:
1、json.dumps序列化時對中文默認使用的ascii編碼.想輸出真正的中文需要指定ensure_ascii=False: 2、實例代碼: import json dict = {'key': '測試'} print(json.dumps(dict)) # 輸出:{"key": "\u6d4b\u8bd5"}
(2)解決方法:
方法: json.dumps(dict,ensure_ascii = False) 代碼: import json dict = {'key': '測試'} print(json.dumps(dict, ensure_ascii=False)) #輸出: {"key": "測試"}
6、AttributeError: ‘NoneType’ object has no attribute ‘decode’
(1)原因:
1、redis數據庫為空,未取到數據,返回類型是NoneType類型 2、錯誤實例: from redis import StrictRedis r = StrictRedis(host='localhost', port=6379) print(r.lpop('test').decode('utf-8'))
(2)解決方法:
1、確保redis里面有數據,先存數據,再取數據 2、代碼: import json from redis import StrictRedis r = StrictRedis(host='localhost', port=6379) dict = {'key': '測試'} data = json.dumps(dict, ensure_ascii=False) r.rpush('test', data) print(r.lpop('test').decode('utf-8')) #redis取出來的數據為字節類型,需要編碼decode #輸出:{"key": "測試"}
7、如果代理設置成功,最后顯示的IP應該是代理的IP地址,但是最終還是我真實的IP地址,為什么?
獲取代理並檢測有效性:https://github.com/Shirmay1/Python/blob/master/Proxyip/xici.py
(1)原因:
requests.get(url,headers=headers,proxies=proxies) 1、proxies在你訪問http協議的網站時用http代理,訪問https協議的網站用https的代理 2、所以你的proxy需要根據網站是http還是https的協議進行代理設置,這樣才能生效
(2)解決方法:
1、方法: 如果proxies ={'http': 'http://112.85.169.206:9999'}, http的測試網站'http://icanhazip.com' 如果proxies ={'https': 'https://112.85.129.89:9999'} https的測試網站https://www.baidu.com/ 2、代碼: proxies ={'http': 'http://112.85.169.206:9999'} r = requests.get('http://icanhazip.com', headers=headers, proxies=proxies, timeout=2) proxies ={'https': 'https://112.85.129.89:9999'} r = requests.get('https://www.baidu.com/', headers=headers, proxies=proxies, timeout=2) ···
8、HTTPConnectionPool
(1) Max retries exceeded with url
1、報錯: a.HTTPConnectionPool(host='XXX', port=XXX): Max retries exceeded with url: XXXX(Caused by ProxyError('Cannot connect to proxy.', ConnectionResetError(10054, '遠程主機強迫關閉了一個現有的連接。', None, 10054, None))) b.HTTPConnectionPool(host='XXX', port=XXX): Max retries exceeded with url: XXXX(Caused by ProxyError('Cannot connect to proxy.', NewConnectionError('<urllib3.connection.HTTPConnection object at 0x0000020B87AAC4E0>: Failed to establish a new connection: [WinError 10061] 由於目標計算機積極拒絕,無法連接。'))) c.HTTPConnectionPool(host='XXX', port=XXX): Max retries exceeded with url: XXXX (Caused by ProxyError('Cannot connect to proxy.', RemoteDisconnected('Remote end closed connection without response'))) 2、原因: a.http連接太多沒有關閉導致 b.訪問次數頻繁,被禁止訪問 c.每次數據傳輸前客戶端要和服務器建立TCP連接,為節省傳輸消耗,默認為keep-alive,即連接一次,傳輸多次,然而在多次訪問后不能結束並回到連接池中,導致不能產生新的連接 3、解決方法: a.增加連接次數 b.requests使用了urllib3庫,默認的http connection是keep-alive的,requests設置False關閉。 c.headers中的Connection默認為keep-alive,將headers中的Connection一項置為close 4、小知識補充: a.會話對象requests.Session能夠跨請求地保持某些參數,比如cookies,即在同一個Session實例發出的所有請求都保持同一個cookies,而requests模塊每次會自動處理cookies,這樣就很方便地處理登錄時的cookies問題。
(2)代碼
"""增加重連次數,關閉多余連接,使用代理""" import requests headers = {'Connection': 'close'} requests.adapters.DEFAULT_RETRIES = 5 # 增加重連次數 s = requests.session() s.keep_alive = False # 關閉多余連接 s.proxies = {"https": "47.100.104.247:8080", "http": "36.248.10.47:8080", } # 使用代理 try: s.get(url) # 訪問網址 except Exception as err: pass """This will GET the URL and retry 3 times in case of requests.exceptions.ConnectionError. backoff_factor will help to apply delays between attempts to avoid to fail again in case of periodic request quo""" import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry session = requests.Session() retry = Retry(connect=3, backoff_factor=0.5) adapter = HTTPAdapter(max_retries=retry) session.mount('http://', adapter) session.mount('https://', adapter) try: s.get(url) # 訪問網址 except Exception as err: pass
歡迎關注公眾號:Python爬蟲數據分析挖掘,回復【開源源碼】免費獲取更多開源項目源碼
公眾號每日更新python知識和【免費】工具