爬取微博圖片數(shù)據(jù)存到Mysql中遇到的各種坑\爬取微博圖片\Mysql存儲圖片\微博爬蟲

本人長期出售超大量微博數(shù)據(jù)叫挟、旅游網(wǎng)站評論數(shù)據(jù),并提供各種指定數(shù)據(jù)爬取服務(wù),Message to YuboonaZhang@Yahoo.com袖肥。同時歡迎加入社交媒體數(shù)據(jù)交流群:99918768

前言

??由于硬件等各種原因需要把大概170多萬2t左右的微博圖片數(shù)據(jù)存到Mysql中.之前存微博數(shù)據(jù)一直用的非關(guān)系型數(shù)據(jù)庫mongodb,由于對Mysql的各種不熟悉振劳,踩了無數(shù)坑椎组,來來回回改了3天才完成。

挖坑填坑之旅

建表

存數(shù)據(jù)的時候首先需要設(shè)計數(shù)據(jù)庫,我準備設(shè)計了3個表

微博表:[id, userid, blog_text, lat, lng, created_time, reserve]?? pkey: id

圖片表:[md5, pic_url, pic_bin, exif, reserve]?? pkey: md5

關(guān)系表:[id, md5, reserve]?? pkey: (id, md5) ? fkey: (id, 微博表id)? (md5, 圖片表md5)

??建表的時候別的問題都還好历恐,主要是 pic_bin 的類型和 blog_text 的類型有很大的問題寸癌,首先是pic_bin的類型,開始設(shè)置的為BLOB弱贼,但是運行之后發(fā)現(xiàn)BLOB最大只能存1M的數(shù)據(jù)蒸苇,并不能滿足微博圖片的存儲,后改成MEDIUMBLOB(16M)基本能夠滿足要求了吮旅。再后來就是blog_text溪烤,我遇到的第一個大坑

??開始的時候很自然的設(shè)置blog_text的類型為TEXT味咳,但跑起來發(fā)現(xiàn)有些數(shù)據(jù)存不進去,會報錯檬嘀,經(jīng)篩查發(fā)現(xiàn)是有些微博文本中包含了emoji表情...隨后找了很多資料發(fā)現(xiàn)是因為utf8下文字是三字節(jié)槽驶,但是emoji是四字節(jié),需要將編碼改成utf8mb4鸳兽。然而我在mac上整mysql的配置文件報各種奇葩錯誤,一怒之下把TEXT改成了BLOB掂铐,就好了。因為本地是MAC揍异,我要連接到遠程的一臺Windows上才能通過那個Windows連接到群暉的Mysql上...本地配置改了也白改全陨。

存圖片

??然后這就是一個大坑!V灾馈辱姨! 由于我使用的python3,所以讀取圖片得到的二進制的結(jié)果前面會有一個b', 表示bytes棍鳖,正是由于這個b'導致sql語句拼接的時候這個b后面的單引號會和sql語句的引號結(jié)合炮叶,導致后面的二進制沒有在引號里面出錯!二進制編碼又不像string可以對字符轉(zhuǎn)義渡处,試了好多方法都不行镜悉!最后沒有辦法使用base64 對二進制進行加密轉(zhuǎn)化成字符串,存到數(shù)據(jù)庫中医瘫,然后要用時的時候再解密侣肄。

pic_bin = str(base64.b64encode(pic_bin))[2:-1]

改配置文件

??由于使用Python多進程,一個小時8G數(shù)據(jù)量,圖片數(shù)據(jù)比較大醇份,發(fā)包的時候回超過mysql的默認限制稼锅,出現(xiàn)Mysql server has gone away, 這個時候要改配置文件,在配置文件中參數(shù)

max_allowed_packet = 600M
wait_timeout = 60000

Lost connection to Mysql server during query

??程序跑著跑著總會出現(xiàn)這個錯誤僚纷,一直找原因矩距,試了各種辦法看了好多資料,一直都是錯誤怖竭。實在不知道什么原因了...后來一想锥债,我管他什么原因,失去連接之后重新連接就行了痊臭。使用conn.Ping(True) 判斷是否連接mysql成功哮肚。如果失去連接就重新連接就行了!最后解決了這個問題

代碼實現(xiàn)

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Created by Baoyi on 2017/10/16
from multiprocessing.pool import Pool

import pymysql
import requests
import json
import exifread
from io import BytesIO
import configparser
import hashlib
import logging
import base64

# 配置logging
logging.basicConfig(level=logging.WARNING,
                    format='%(asctime)s %(filename)s[line:%(lineno)d] %(levelname)s %(message)s',
                    datefmt='%a, %d %b %Y %H:%M:%S',
                    filename='weibo.log',
                    filemode='w')

cf = configparser.ConfigParser()
cf.read("ConfigParser.conf")

# 讀取配置mysql
db_host = cf.get("mysql", "db_host")
db_port = cf.getint("mysql", "db_port")
db_user = cf.get("mysql", "db_user")
db_pass = cf.get("mysql", "db_pass")
db = cf.get("mysql", "db")

# 創(chuàng)建連接
conn = pymysql.connect(host=db_host, user=db_user, passwd=db_pass, db=db, port=db_port, charset='utf8')
# 獲取游標
cursor = conn.cursor()

# 創(chuàng)建insert_sql
insert_blog_sql = (
    "INSERT IGNORE INTO blog(userid, id, blog_text, lat, lng, created_time) VALUES('{uid}', '{id}','{blog_text}','{lat}','{lng}','{created_time}')"
)

insert_pic_sql = (
    "INSERT IGNORE INTO pics(pic_url, pic_bin, md5, exif) VALUES ('{pic_url}','{pic_bin}','{md5}','{exif}')"
)

insert_relationship_sql = (
    "INSERT IGNORE INTO relationship(id, md5) VALUES ('{id}','{md5}')"
)

uid = []

with open('./data/final_id.txt', 'r') as f:
    for i in f.readlines():
        uid.append(i.strip('\r\n'))


# 處理圖片數(shù)據(jù)
def handle_pic(pic_url):
    large_pic_url = pic_url.replace('thumbnail', 'large')
    large_bin = requests.get(large_pic_url)
    return large_bin.content


def get_poiid_info(uid):
    try:
        url = 'https://api.weibo.com/2/statuses/user_timeline.json'
        load = {
            'access_token': 'xxxxxxxxxx',
            'uid': uid,
            'count': 100,
            'feature': 2,
            'trim_user': 1
        }
        get_info = requests.get(url=url, params=load, timeout=(10, 10))
        if get_info.status_code != 200:
            logging.warning(ConnectionError)
            pass
        info_json = json.loads(get_info.content)
        info_json['uid'] = uid
        statuses = info_json['statuses']
        # 處理篩選微博數(shù)據(jù)
        for status in statuses:
            id = status['idstr']
            if status['geo'] is not None:
                lat = status['geo']['coordinates'][0]
                lng = status['geo']['coordinates'][1]
                pic_urls = status['pic_urls']

                # 判斷是否在北京
                if (115.7 < lng < 117.4) and (39.4 < lat < 41.6):
                    # 若在北京,插入blog數(shù)據(jù)進庫
                    blog_text = status['text'].replace('\'', '\'\'')
                    created_time = status['created_at']
                    try:
                        cursor.execute(
                            insert_blog_sql.format(uid=uid, id=id, blog_text=blog_text, lat=lat, lng=lng,
                                                   created_time=created_time))
                    except pymysql.err.OperationalError as e_blog:
                        logging.warning(e_blog.args[1])
                        pass

                    # conn.commit()
                    # 處理圖片
                    for pic_url in pic_urls:
                        # 獲取原圖片二進制數(shù)據(jù)
                        pic_bin = handle_pic(pic_url['thumbnail_pic'])

                        # 讀取exif 數(shù)據(jù)
                        pic_file = BytesIO(pic_bin)  # 將二進制數(shù)據(jù)轉(zhuǎn)化成文件對象便于讀取exif數(shù)據(jù)信息和生成MD5
                        tag1 = exifread.process_file(pic_file, details=False, strict=True)
                        tag = {}
                        for key, value in tag1.items():
                            if key not in (
                                    'JPEGThumbnail', 'TIFFThumbnail', 'Filename',
                                    'EXIF MakerNote'):  # 去除四個不必要的exif屬性广匙,簡化信息量
                                tag[key] = str(value)
                        tags = json.dumps(tag)  # dumps為json類型 此tag即為exif的json數(shù)據(jù)
                        # 生成MD5
                        MD5 = hashlib.md5(pic_file.read()).hexdigest()
                        # 首先把二進制圖片用base64 轉(zhuǎn)成字符串之后再存
                        try:
                            cursor.execute(
                                insert_pic_sql.format(pic_url=pic_url['thumbnail_pic'].replace('thumbnail', 'large'),
                                                      pic_bin=str(base64.b64encode(pic_bin))[2:-1], md5=MD5,
                                                      exif=tags))
                        except pymysql.err.OperationalError as e_pic:
                            logging.warning(e_pic.args[1])
                            pass
                        try:
                            cursor.execute(insert_relationship_sql.format(id=id, md5=MD5))
                        except pymysql.err.OperationalError as e_relation:
                            logging.warning(e_relation)
                            pass
                        conn.commit()

                else:
                    logging.info(id + " is Not in Beijing")
                    pass
            else:
                logging.info(id + ' Geo is null')
                pass
    except pymysql.err.OperationalError as e:
        logging.error(e.args[1])
        pass


def judge_conn(i):
    global conn
    try:
        conn.ping(True)
        get_poiid_info(i)
    except pymysql.err.OperationalError as e:
        logging.error('Reconnect')
        conn = pymysql.connect(host=db_host, user=db_user, passwd=db_pass, db=db, charset='utf8')
        get_poiid_info(i)


def handle_tuple(a_tuple):
    read_uid_set = []
    for i in a_tuple:
        read_uid_set.append(i[0])
    return set(read_uid_set)


if __name__ == '__main__':
    sql_find_uid = (
        "SELECT userid FROM blog"
    )
    cursor.execute(sql_find_uid)
    read_uid_tuple = cursor.fetchall()
    read_list = handle_tuple(read_uid_tuple)
    print(len(read_list))

    new_uid = set(uid).difference(read_list)
    print(len(new_uid))

    pool = Pool()
    pool.map(judge_conn, list(new_uid))

個人博客

8aoy1.cn

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末允趟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鸦致,更是在濱河造成了極大的恐慌潮剪,老刑警劉巖涣楷,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鲁纠,居然都是意外死亡总棵,警方通過查閱死者的電腦和手機鳍寂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門改含,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人迄汛,你說我怎么就攤上這事捍壤。” “怎么了鞍爱?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵鹃觉,是天一觀的道長。 經(jīng)常有香客問我睹逃,道長盗扇,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任沉填,我火速辦了婚禮疗隶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翼闹。我一直安慰自己斑鼻,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布猎荠。 她就那樣靜靜地躺著坚弱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪关摇。 梳的紋絲不亂的頭發(fā)上荒叶,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音输虱,去河邊找鬼些楣。 笑死,一個胖子當著我的面吹牛悼瓮,可吹牛的內(nèi)容都是我干的戈毒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼横堡,長吁一口氣:“原來是場噩夢啊……” “哼埋市!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起命贴,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤道宅,失蹤者是張志新(化名)和其女友劉穎食听,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體污茵,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡樱报,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了泞当。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片迹蛤。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖襟士,靈堂內(nèi)的尸體忽然破棺而出盗飒,到底是詐尸還是另有隱情,我是刑警寧澤陋桂,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布逆趣,位于F島的核電站,受9級特大地震影響嗜历,放射性物質(zhì)發(fā)生泄漏宣渗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一梨州、第九天 我趴在偏房一處隱蔽的房頂上張望痕囱。 院中可真熱鬧,春花似錦摊唇、人聲如沸咐蝇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽有序。三九已至,卻和暖如春岛请,著一層夾襖步出監(jiān)牢的瞬間旭寿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工崇败, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盅称,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓后室,卻偏偏與公主長得像缩膝,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子岸霹,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 《高性能MySQL》&《MySQL技術(shù)內(nèi)幕 InnoDB存儲引擎》筆記 第一章 MySQL架構(gòu)與歷史 MySQL的...
    xiaogmail閱讀 12,773評論 0 39
  • MySQL技術(shù)內(nèi)幕:InnoDB存儲引擎(第2版) 姜承堯 第1章 MySQL體系結(jié)構(gòu)和存儲引擎 >> 在上述例子...
    沉默劍士閱讀 7,417評論 0 16
  • 什么是數(shù)據(jù)庫疾层? 數(shù)據(jù)庫是存儲數(shù)據(jù)的集合的單獨的應(yīng)用程序。每個數(shù)據(jù)庫具有一個或多個不同的API贡避,用于創(chuàng)建痛黎,訪問予弧,管理...
    chen_000閱讀 4,035評論 0 19
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)湖饱,斷路器掖蛤,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 概述 默認情況下,Android應(yīng)用程序是沒有任何授權(quán)的井厌,當應(yīng)用程序需要使用設(shè)備的任何受保護功能(發(fā)送網(wǎng)絡(luò)請求蚓庭,訪...
    CP9閱讀 1,053評論 0 1