爬蟲中常見問題


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知識和【免費】工具

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM