1. 程式人生 > >使用redis所維護的代理池抓取微信文章

使用redis所維護的代理池抓取微信文章

sge article pri data item referer count ttr fail

搜狗搜索可以直接搜索微信文章,本次就是利用搜狗搜搜出微信文章,獲得詳細的文章url來得到文章的信息.並把我們感興趣的內容存入到mongodb中。

因為搜狗搜索微信文章的反爬蟲比較強,經常封IP,所以要在封了IP之後切換IP,這裏用到github上的一個開源類,當運行這個類時,就會動態的在redis中維護一個ip池,並通過flask映射到網頁中,可以通過訪問 localhost:5000/get/

來獲取IP

這是搜狗微信搜索的頁面,

技術分享圖片

構造搜索url .搜索時會傳遞的參數,通過firefox瀏覽器可以查到:

技術分享圖片

代碼如下,

from urllib.parse import urlencode
import requests from settings import * from requests.exceptions import ConnectionError from pyquery import PyQuery as pq import pymongo from lxml.etree import XMLSyntaxError class WeixinArticle(object): """ 通過搜狗搜索微信文章,爬取到文章的詳細內容,並把我們感興趣的內容存入到mongodb中 因為搜狗搜索微信文章的反爬蟲比較強,經常封IP,所以要在封了IP之後切換IP,這裏用到github上的一個開源類, 當運行這個類時,就會動態的redis中維護一個ip池,並通過flask映射到網頁中,可以通過訪問 localhost:5000/get/ 來獲取IP,
""" # 定義一個全局變量,這樣在各個函數中均可以使用,就不用在傳遞這個變量 proxy = None def __init__(self): self.keyword = KEYWORD self.proxy_pool_url = PROXY_POOL_URL self.max_count = MAX_COUNT self.mongo_url = MONGO_URL self.mongo_db = MONGO_DB self.base_url = BASE_URL self.headers
= HEADERS self.client = pymongo.MongoClient(self.mongo_url) self.db = self.client[self.mongo_db] def get_proxy(url): """獲取我們所維護的IP""" try: response = requests.get(url) if response.status_code == 200: return response.text return None except ConnectionError: return self.get_proxy(url) def get_html(url, count=1): """ 通過傳的url 獲取搜狗微信文章頁面信息, :param count: 表示最後遞歸的次數 :return: 頁面的信息 """ if not url: return None if count >= self.max_count: print("try many count ") return None print(crowling url , url) print(crowling count , count) # 定義一個全局變量,這樣在各個函數中均可以使用,就不用在傳遞這個變量 global proxy try: if proxy: # 如果有代理就用代理去訪問 proxy = { "http": "http://" + proxy, } response = requests.get(url=url, allow_redirects=False, headers=self.headers, proxy=proxy) else: # 如果沒有代理就不用代理去訪問 response = requests.get(url=url, allow_redirects=False, headers=self.headers) if response.status_code == 200: return response.text elif response.status_code == 302: # 如果重定向了,說明我們的IP被ban了,此時就要更換代理 print("status code is 302 ") proxy = self.get_proxy(self.proxy_pool_url) if proxy: print("Useing proxy: ", proxy) return self.get_html(url) else: # 如果我們的代理都獲取失敗,那這次爬取就完全失敗了,此時返回None print("Get proxy failed") return None else: print("Occur Error : ", response.status_code) except ConnectionError as e: #我們只是遞歸調用max_count 這麽多次,防止無限遞歸 print("Error Occured ", e.args) count += 1 proxy = self.get_proxy(self.proxy_pool_url) return self.get_html(url, count) def get_index(keyword, page): """use urlencode to constract url""" data = { "query": keyword, "type": 2, # "s_from": "input", # "ie": "utf8", "page": page } queries = urlencode(data) url = self.base_url + queries # 為了讓程序的的流程更直觀,可以不在這裏調用 get_html函數,在main函數中調用 # print(url) # html = get_html(url) # return html return url def parse_index_html(html): """解析get_html函數得到的html,即是通過關鍵字搜索的搜狗頁面,解析這個頁面得到微信文章的url""" if not html: """如果傳進來的html是None,則直接返回""" print("the html is None") return None doc = pq(html) items = doc(".news-list li .txt-box h3 a").items() for item in items: article_url = item.attr("href") yield article_url def get_detail(url): """通過parse_index函數得到微信文章的url,利用requests去得到這個url的網頁信息""" try: response = requests.get(url) if response.status_code == 200: return response.text return None except ConnectionError: return None def parse_detail(html): """解析通過get_detail函數得到微信文章url網頁信息,得到我們想要的信息""" if not html: return None try: doc = pq(html) title = doc(#activity-name).text() content = doc(".rich_media_content p span").text() date = doc("#post-date").text() weichat_name = doc("#post-user").text() return { "title": title, "content": content, "date": date, "weichat_name": weichat_name, } except XMLSyntaxError: return None def save_to_mongo(data): """保存到mongodb 中,這裏我們希望 title 不重復,所以用update 這種方式進行更新""" if self.db[articls].update({"title": data["title"]}, {"$set": data}, True): print("save to mongo ") else: print("save to mongo failed ", data["title"]) def main(): """ 爬蟲的主要邏輯就在main函數中, 在所有的處理函數中都加了異常處理,和當傳入的值是None時的處理,所以在main函數就不用再做處理 """ keyword = self.keyword for page in range(1, 5): url = self.get_index(keyword, page) html = self.get_html(url) article_url = self.parse_index_html(html) article_html = self.get_detail(article_url) article_data = self.parse_detail(article_html) self.save_to_mongo(article_data) def run(self): self.main() if __name__ == "__main__": weixin_article = WeixinArticle()  weixin_article.run()

之後,把這們常用的配置寫在settings.py,方便程序的修改。

KEYWORD="python"
PROXY_POOL_URL="http://127.0.0.1:5000"
MAX_COUNT=5
MONGO_URL="localhost"
MONGO_DB="weixin"
BASE_URL = "http://weixin.sogou.com/weixin?"
HEADERS = {
    # "Accept": "* / *",
    # "Accept-Encoding": "gzip, deflate",
    # "Accept-Language": "zh-CN,zh;q=0.9",
    # "Connection": "keep-alive",
    "Cookie": "SUID=2FF0747C4990A000000005A45FE0C; SUID=59A4FF3C52110A000000005A45FE0D; weixinIndexVisited=1; SUV=00E75AA0AF9B99675A45FE104A7DA232; CXID=B3DDF3C9EA20CECCB4F94A077C74ED; ad=Xkllllllll0U1lllllVIKlBclllllNllll9lllll9qxlw@@@@@@@@@@@; ABTEST=0|1517209704|v1; SNUID=D8A7384B3C395FD9D44BC22C3DDE2172; JSESSIONID=aaa3zBm9sI7SGeZhCaCew; ppinf=5|1517210191|1518419791|dHJ1c3Q6MToxfGNsaWVudGlkOjQ6MjAxN3x1bmlxbmFtZToxODolRTYlODElODElRTYlODAlODJ8Y3J0OjEwOjE1MTcyMTAxOTF8cmVmbmljazoxODolRTYlODElODElRTYlODAlODJ8dXNlcmlkOjQ0Om85dDJsdUN1Rm5aVTlaNWYzMGpjNVRDUGN6NW9Ad2VpeGluLnNvaHUuY29tfA; pprdig=m",
    "Host": "account.sogou.com",
    "Referer": "http://weixin.sogou.com/weixin?query=python&s_from=input&type=2&page=10&ie=utf8",
    "Upgrade-Insecure-Requests": "1",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
}

項目目錄結構如下:

技術分享圖片

關於怎麽使用redis 維護一個代理池,見

使用redis所維護的代理池抓取微信文章