微信公众号爬虫(综合解决方案)

最终解决方案

通过搜狗微信先检索公众号,获取公众号主页链接,接着爬每一篇具体文章,具体用selenium实现,当然你也可以用webkit、Geoko渲染引擎自己去渲染。用selenium、webkit、Geoko能省去分析网页Js Ajax部分加载逻辑。关于selenium的一些常用操作,后续抽个时间单独写一篇博文儿~。

一般公司内部会部署自己爬虫平台,通过代理池能最终解决此问题,当然也可以用免费开源项目IPProxyPool利用免费爬到的IP构建代理池。在没有用代理池遇到验证码反爬措施时,我们自动识别并填写并维护一个cookie池,即能降低验证码出现的频率。

遇到验证码时的理想状态是自动识别,可通过购买现成接口或自己用CNN训练个模型。本文用的是人肉识别手动填写…

难点

  1. 通过公众号检索页面检索时,超过一定次数后就会出现验证码。
  2. 公众号主页的链接URL会失效。
  3. 通过搜狗微信平台检索出的结果页面HTML数据中,匹配到的公众主页URL不能直接而访问,直接访问会被反爬措施限制,需要经过js处理生成最终的请求URL才能访问(抓包的结果是最终的请求URL)。

现有解决方案

  1. Charles移动端、PC端抓包,获取公众号主页加密链接,再后续爬取。但得到的链接可能会失效,失效后还是需要人工手动去分析抓包结果,提取URL,无法做到自动化工程级别的微信爬虫。
  2. 登录自己公众号,通过“检索功能”爬取其他公众号的文章。但检索有上限,500篇一天。
  3. 八爪鱼模板傻瓜抓取。但高级功能收费,同样无法实现工程级别的部署,玩玩还是可以的 。
  4. 自动化测试工具(如Airtest,基于图像识别和poco控件识别的一款UI自动化测试工具)网易团队刚出的,挺新奇,移动端、PC都能抓,有些许bug,可以当做code版免费八爪鱼。同样不太适用于工程级别的部署。

遇到验证码的解决办法

正确的解决办法是识别并破解,但这里我们可以直接通过selenium截图保存本地,人肉识别并把结果输入值CommandLine,再把cookie记录下来维护个cookie池,以后换着用。不过这并不是一个永久的解决方法,只不过苦于时间紧迫,没时间去整验证码识别相关的东西,大家有时间可以学习下CNN训练验证码识别模型。如下实现思路是,判断返回页面中是否存在验证码,若有则down到本地,识别后命令行输入。

from splinter import Browser
from selenium import webdriver
def get_html(url):
    with Browser("chrome", incognito=True,
                 headless=True) as browser:
        browser.driver.set_page_load_timeout(400)
        browser.driver.set_window_size(1200, 900)
        browser.visit(url)

        if "验证码" in browser.html:
            now = datetime.datetime.now()
            isValidCode = False
            while isValidCode != True:
                # screen shot
                browser.screenshot("/home/jiangbowen/wechat_identifying_code/codeToBeDealWith%s.png" % now)
                # manually input code
                code = input("你gg了, please inpute code:")
                code_imput = browser.find_by_id("seccodeInput")
                code_imput[0].click()
                code_imput[0].fill(code)
                submit_btn = browser.find_by_id("submit")
                time.sleep(3)

                code_imput[0].click()
                time.sleep(1)
                code_imput[0].fill(code)
                browser.screenshot("/home/jiangbowen/wechat_identifying_code/afterFillingCode%s.png" % now)
                time.sleep(1)
                submit_btn[0].click()
                # be careful
                time.sleep(7)
                browser.screenshot("/home/jiangbowen/wechat_identifying_code/afterclick%s.png" % now)

                print(browser.driver.current_url)
                return browser.html
        return browser.html

特别提醒:
在模拟填入验证码并点击提交后,需要我们等待一段时间,用于留给微信后端验证,此过程较长,建议5s以上(填了很久才发现这个鬼原因)。否则通过browser.driver.current_url获取到的仍然是验证页面的URL,而不是我们想要的检索页面。

每一步的ScreenShot如下:
在这里插入图片描述
可以看到验证成功!
在这里插入图片描述

保存Cookie的操作(建立Cookie池,略过此步亦可)

用pickle来实现cookie的存取,注意selenium在设置Cookie时需要先加载网站
Login成功后保存Cookie:

import pickle

# save cookie after login
cookiess = browser.driver.get_cookies()
# overwrite
if not os.path.exists(LOCAL_COOKIE_PATH):
    os.mkdir(LOCAL_COOKIE_PATH)
pickle.dump(cookiess, open(LOCAL_COOKIE_FILE, "wb"))

请求前读取Cookie:

LOCAL_COOKIE_PATH = "./cookies"
LOCAL_COOKIE_FILE = "./cookies/cookie_qingbo.pkl"

# add cookie
try:
    # we should load website firstly
    browser.driver.get("weixin.sogou.com")
    loc_cookies = pickle.load(open(LOCAL_COOKIE_FILE, "wb"))
    browser.driver.delete_all_cookies()

    for cookie in loc_cookies:
        cookie_dict = {}
        cookie_dict['httpOnly'] = cookie.get('httpOnly')
		# set other cookie items
        browser.driver.add_cookie(cookie_dict)

    browser.driver.refresh()

except Exception as e:
    print("cookie parse error")


具体解析微信文章

根据公众号主页URL获取该页面下所有的文章Title与URL:

def get_titleurls_by_account_homepage_url(account_home_url):
    """
    get_titles_urls_by_account_home_url

    :param account_home_url: public account home page's url
    :return: list of tuple (title, url)
    """
    main_page_html = get_html(account_home_url)
    account_name_reg = r"(?<=<title>)([\s\S]*?)(?=</title>)"
    url_title_reg = r"<div class=\"weui_media_bd\">([\s\S]*?)</h4>"
    url_sub_reg = r"(?<= hrefs=\")([\s\S]*?)(?=\">)"
    title_sub_reg = r"=\">([\s\S]*?)(?=</h4>)"  # need to remove top 3 char

    account_name = re.findall(account_name_reg, main_page_html)[0]
    url_title_arr = re.findall(url_title_reg, main_page_html)

    title_url_list = []
    for url_title in url_title_arr:
        article_url = "https://mp.weixin.qq.com" + re.findall(url_sub_reg ,url_title)[0].replace(";" ,"&")
        # remove <span> tag
        article_title = re.sub(r"(?=<span)[\s\S]*?</span>", '', url_title).strip()
        # remove HTML tags
        article_title = re.sub(r"<[^>]+>", '', article_title).strip()
        # remove blank
        article_title = re.sub(r"\n*  *   *    *", '', article_title).strip()

        title_url_list.append((article_title, article_url))
    return title_url_list

根据公众号文章链接解析内容


def scrape_url_content(url, title, account_name):
    """
    get wechat article detail content

    :param url: url
    """
    html = get_html(url)
    article = {}

    try:
        publish_time = re.findall(r"(?<=publish_time = \")[\s\S]*?(?=\")", html)[0]
        # Html that we care about
        tmp = re.findall('(?<=js_content">)[\s\S]*?(?=<script nonce)', html)[0]
        # remove html tag except <img>
        tmp = re.sub(r"<(?!img)[^>]*>", '', tmp)
        # tmp = re.sub(r"(<(?!/?(p|img|span)(\s|>))[^>]*>)", '', tmp) # except p/img/span
        # remove blank
        tmp = re.sub(r"\n*  *   *    *", '', tmp)
        # remove <i> tags
        tmp = re.sub(r"<i [\w].*?>", '', tmp)
        cleaned_content_html = tmp
    except Exception as e:
        return

    article["body"] = cleaned_content_html
    article["title"] = title
    article["source"] = account_name
    article["publish_time"] = publish_time

    return article

最终效果:

图片会保留原始标签,大家可自行处理。
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值