Joeyos's Blog Software Engineer

爬取微博Ajax数据

2019-05-15
Quan Zhang

有时候在浏览器可以看到正常的网页数据,但是requests得不到数据,这是由于数据可能是通过Ajax加载的,也可能是经过JS和特定算法计算后生成的。

浏览网页的时候,我们会发现很多网页都有“下滑查看更多”的选项,这个过程就是Ajax加载的过程。【都是通过JS实现】

  • 发送请求【Type为XHR】
  • 解析内容
  • 渲染网页

爬取微博

分析请求

是一个get请求,请求链接为:https://m.weibo.cn/api/container/getIndex?

后面跟着5个参数:

  • uid
  • luicode
  • lfid
  • containerid,爬取谁的微博
  • page,指定获取哪个页面

分析响应

响应为json数据,data/cards/mblog下为响应内容:

  • id
  • text为微博文字
  • attitudes_count为点赞人数
  • reposts_count为转发数
  • comments_count为评论人数

图片存在data/cards/mblog/pics/i/url下面,其中i为0-8数字。

抓取页面

# -*- coding: utf-8 -*-
from urllib.parse import urlencode
from pyquery import PyQuery as pq
from pymongo import MongoClient
import requests
import os

base_url = 'https://m.weibo.cn/api/container/getIndex?'
headers={
    'Referer': 'https://m.weibo.cn/u/1749127163?uid=1749127163&luicode=10000011&lfid=100103type%3D1%26q%3D%E9%9B%B7%E5%86%9B',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36',
    'X-Requested-With':'XMLHttpRequest',
}

def get_page(page):
    params={
        'uid': '1749127163',
        'luicode': '10000011',
        'lfid': '100103type=1&q=雷军',
        'containerid': '1076031749127163',
        'page': page
    }
    url=base_url+urlencode(params)
    try:
        response=requests.get(url,headers=headers)
        if response.status_code==200:
            return response.json()
    except requests.ConnectionError as e:
        print('Error',e.args)

#解析网页
def parse_page(json):
    if json:
        items=json.get('data').get('cards')
        for item in items:
            item = item.get('mblog')
            #首页cards第二条不含mblog微博信息,要跳过
            if item == None:
                continue
            #图片
            pics=item.get('pics')
            imgs={}
            if pics!=None:
                for pic in pics:
                    imgs[pic.get('pid')] = pic.get('large').get('url')
            yield {
                'id':item.get('id'),
                'text':pq(item.get('text')).text(),
                'attitudes':item.get('attitudes_count'),
                'comments':item.get('comments_count'),
                'reposts':item.get('reposts_count'),
                'imgs':imgs
            }
            
#保存图片
def save_image(mid,pid,url):
    if not os.path.exists(mid):
        os.mkdir(mid)
    try:
        response=requests.get(url)
        if response.status_code==200:
            file_path='{0}/{1}.{2}'.format(mid,pid,'jpg')
            with open(file_path,'wb') as f:
                f.write(response.content)
    except requests.ConnectionError:
        print('Failed to save image...')

if __name__ == '__main__':
    #保存信息
    client = MongoClient()
    db = client.weibo#指定数据库
    collection = db.weibo#指定集合
    collection.delete_many({})#删除所有
    #抓取1和2页的微博
    for page in range(1,3):
        json=get_page(page)
        results=parse_page(json)
        for result in results:
            #保存到数据库
            collection.insert(result)
            #保存图片
            imgs=result['imgs']
            for it in imgs:
                save_image(result['id'],it,imgs[it])
    #检查数据库数据
    outs=collection.find({})
    for it in outs:
        print(it)
    print('微博数目:',collection.count_documents({}))
"""
抓取结果如下,每页10条,这里只显示3条
{'_id': ObjectId('5cdae308a9ab7b822478f045'), 'id': '4368918258900032', 'text': '小米9全息幻彩色,真棒!', 'attitudes': 2466, 'comments': 1615, 'reposts': 189, 'imgs': {}}
{'_id': ObjectId('5cdae308a9ab7b822478f046'), 'id': '4371901444759517', 'text': '我试试#Redmi K20# ,非常好!', 'attitudes': 1411, 'comments': 823, 'reposts': 78, 'imgs': {}}
{'_id': ObjectId('5cdae308a9ab7b822478f047'), 'id': '4371814035208997', 'text': 'Flagship Smartphone Killer', 'attitudes': 1822, 'comments': 1055, 'reposts': 120, 'imgs': {}}
"""

存储信息

from pymongo import MongoClient
client = MongoClient()
db = client['weibo']
collection = db['weibo']

def save_to_mongo(result):
    if collection.insert(result):
        print('Saved to Mongo...')

多线程

如下多线程存在线程安全,存入数据库的数据有重复,最终微博条数变少。

from multiprocessing.pool import Pool

def main(page):
    #保存信息
    client = MongoClient()
    db = client.weibo#指定数据库
    collection = db.weibo#指定集合
    #collection.delete_many({})#删除所有
    json=get_page(page)
    results=parse_page(json)
    for result in results:
        #保存到数据库
        collection.insert(result)
        #保存图片
        imgs=result['imgs']
        for it in imgs:
            save_image(result['id'],it,imgs[it])
   
if __name__ == '__main__':
    client = MongoClient()
    db = client.weibo#指定数据库
    collection = db.weibo#指定集合
    collection.delete_many({})#删除所有
    pool=Pool()
    pages=([x for x in range(0,2)])
    pool.map(main,pages)
    pool.close()
    pool.join()
    #检查数据库数据
    outs=collection.find({})
    for it in outs:
        print(it)
    print('微博数目:',collection.count_documents({}))

Similar Posts

上一篇 爬虫数据存储

Comments