藉助詩詞API和微博圖床搭建自動發圖文微博機器人
在2011年的時候,浙大的一位博士生藉助微博的開放平臺為他實驗室的一臺飲水機弄了個微博,名喚@浙大CCNT實驗室飲水機,俗稱“飲水機娘“。當年這條新聞給自己留下了挺大的印象,也一直對這個微博賬號可以自動發微博背後的機理感到十分憧憬。一晃,時間都來到了2019年了~~
咳咳——在鑽研不少技術文章以及對技術派網友的多多請教後,自己終於也搗弄了一個自動發微博的機器人( ̄︶ ̄)↗
此微博機器人的功能如下:
- 直接模擬登陸新浪微博;
- 自動獲取唐詩宋詞文字;
- 自動獲取文藝主題圖片;
- 自動上傳圖片至微博圖床;
- 自動傳送內容不同的圖文微博;
- 通過定時任務,實現週期性發微博任務。
實際效果圖:
GitHub倉庫:
https://github.com/Leslie-Won...
正所謂“前不見古人,後不見來者。念天地之悠悠,獨愴然而涕下!“,咳咳——IT技術世界當然不是這樣,我們的技術積累都是站在前人的基礎上的,換言之,站在巨人的肩膀上。所以,還是先來囉嗦囉嗦當年的飲水機娘。
飲水機娘分析
當年果殼網在飲水機娘爆紅了的時候,採訪了背後的開發者——浙江大學電腦科學與技術的一位陳姓博士生。文章標題是 《揭開“飲水機娘”的神祕面紗》 。在這篇文章中,闡述發微博原理的段落如下:
傳送微博的功能通過程式碼實現,利用了新浪微博開放平臺提供的PHP語言軟體開發工具包。在程式碼的設計中,主要有檢測模組和反應模組兩部分。檢測模組處理攝像頭的監控資料,捕捉加熱指示燈“亮->不亮”與“不亮->亮”兩個切換狀態,然後呼叫反應模組及時傳送微博。所以在“飲水機娘”自動傳送的微博下方,會顯示“來自未通過稽核應用”。目前,完成這些功能,所需的程式碼量不足兩百行。
現在來分析分析這兩段話,把整個流程弄成流程圖的話是如下的效果:
從“指示燈”到“視覺演算法判斷狀態”這部分屬於計算機視覺實現了,依本人目前的技術視野判斷,可以藉助openCV來構建。至於傳送微博這一部分,則是純粹的PHP程式碼實現。由於本文所要討論的是構建一個發微博的機器人,而微博報文資料的獲取可以有很多種方式,因此,openCV就點到為止了。(自己也不是太懂openCV)(。・_・)/~~~
自己在查閱了不少技術文獻後,通過這篇 《新浪微博自動(模擬)登陸詳解及實現》 瞭解到飲水機娘傳送的微博下方會出現“來自未通過稽核應用”是由於用了新浪微博開放平臺的介面的緣故,而且其會有幾個比較致命的限制(呼叫次數限制和授權期限限制)。網上流傳一種直接模擬登陸微博的解決方案,關鍵點就是利用php的curl功能,這也是本人所要闡述的微博機器人使用的登入原理。
另外,翻了翻飲水機娘最早期傳送的微博,報告飲水機水沸騰了的微博報文是這樣子的——
後來變成了這樣子——
而對應“亮->不亮”狀態的微博報文最初是這樣子的——
不過,後來關注度上去之後,就很難判斷飲水機娘傳送的微博是不是根據飲水機狀態自動發出去了的了,但是也不影響本文後續的敘述。OK,溯源的部分就到這裡,接下來講講在機器人構建中佔據不少分量的資料獲取API——今日詩詞API、文藝主題圖片API、微博圖床API。
今日詩詞API
今日詩詞API是 亂碼 開發的一個可以返回一句古詩詞名句的介面。它可以通過圖片和JSON格式呼叫。今日詩詞API根據不同地點、時間、節日、季節、天氣、景觀、城市、事件進行智慧推薦。
官方文件地址是 https://www.jinrishici.com/ , 亂碼大佬撰寫的介紹文章則是 https://luan.ma/post/jinrishici/ 。就本人所要構建的微博機器人而言,使用到的介面是 https://v2.jinrishici.com/one... ,而且是使用帶token的呼叫方式。
文藝主題圖片API
這個圖片API是 九凌少子 負責開發的,他的圖源來自於360桌布,主要功能就是根據呼叫需求,返回一張360桌布的官方伺服器上的圖片URL。呼叫方式如下:
https://www.yuluoge.com/api/index.php?cid=5
不同的cid值對應不同的分類,根據他的解釋及本人測試,分類如下——
- cid=0 —— 預設圖片,不分型別
- cid=1 —— 美女
- cid=2 —— 動漫
- cid=3 —— 風景
- cid=4 —— 遊戲
- cid=5 —— 文藝
- cid=6 —— 文字控
- cid=7 —— 動物
- cid=8 —— 愛情
此外,這篇文章最後貼出來的原始碼是基於他在今日詩詞的Q群裡分享的發微博原始碼改造而來的,在此感謝他的貢獻。
微博圖床API
對於微博圖床API的理解得力於這篇文章—— 《利用微博當圖床-php語言實現》 。
其使用到微博圖片上傳介面為
http://picupload.service.weibo.com/interface/pic_upload.php
本文所構建機器人略有改動地使用了這篇文章裡的獲取新浪圖床圖片pid的PHP原始碼。原始碼如下:
/** * 上傳圖片到微博圖床 * @author mengkunhttp://mkblog.cn * @param $file 圖片檔案/圖片url * @param $multipart 是否採用multipart方式上傳 * @return 返回的json資料 */ function upload($file, $multipart = true) { $cookie = '';// 微博cookie $url = 'http://picupload.service.weibo.com/interface/pic_upload.php' .'?mime=image%2Fjpeg&data=base64&url=0&markpos=1&logo=&nick=0&marks=1&app=miniblog'; if($multipart) { $url .= '&cb=http://weibo.com/aj/static/upimgback.html?_wv=5&callback=STK_ijax_'.time(); if (class_exists('CURLFile')) {// php 5.5 $post['pic1'] = new CURLFile(realpath($file)); } else { $post['pic1'] = '@'.realpath($file); } } else { $post['b64_data'] = base64_encode(file_get_contents($file)); } // Curl提交 $ch = curl_init($url); curl_setopt_array($ch, array( CURLOPT_POST => true, CURLOPT_VERBOSE => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array("Cookie: $cookie"), CURLOPT_POSTFIELDS => $post, )); $output = curl_exec($ch); curl_close($ch); // 正則表示式提取返回結果中的json資料 preg_match('/({.*)/i', $output, $match); if(!isset($match[1])) return ''; return $match[1]; }
微博機器人原始碼
列舉了所要用到的幾個重要API,最後還是貼一下機器人的原始碼吧。當然,也有相對應的GitHub倉庫 https://github.com/Leslie-Won...
主模組
//weibo.php <?php require_once './weiboLogin.php'; header("Content-type: text/html; charset=utf-8"); header("Access-Control-Allow-Origin:*"); header('Content-type: application/json'); error_reporting(0); /** 傳送微博 **/ function curl($url,$post=0,$header=0,$cookie=0,$referer=0,$ua=0,$nobody=0){ $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); $httpheader[] = "Accept:*/*"; $httpheader[] = "Accept-Encoding:gzip,deflate,sdch"; $httpheader[] = "Accept-Language:zh-CN,zh;q=0.8"; $httpheader[] = "Connection:close"; curl_setopt($ch, CURLOPT_HTTPHEADER, $httpheader); if($post){ curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); } if($header){ curl_setopt($ch, CURLOPT_HEADER, TRUE); } if($cookie){ curl_setopt($ch, CURLOPT_COOKIE, $cookie); } if($referer){ curl_setopt($ch, CURLOPT_REFERER, $referer); } if($ua){ curl_setopt($ch, CURLOPT_USERAGENT,$ua); }else{ curl_setopt($ch, CURLOPT_USERAGENT,'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36'); } if($nobody){ curl_setopt($ch, CURLOPT_NOBODY,1); } curl_setopt($ch, CURLOPT_ENCODING, "gzip"); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); $ret = curl_exec($ch); curl_close($ch); return $ret; } /** * 上傳圖片到微博圖床 * @author mengkun http://mkblog.cn * @param $file 圖片檔案/圖片url * @param $multipart 是否採用multipart方式上傳 * return 返回的json資料 */ function upload($file, $cookie, $multipart = true){ $url = 'http://picupload.service.weibo.com/interface/pic_upload.php'.'?mime=image%2Fjpeg&data=base64&url=0&markpos=1&logo=&nick=0&marks=1&app=miniblog'; if($multipart){ $url .= '&cb=http://weibo.com/aj/static/upimgback.html?_wv=5&callback=STK_ijax_'.time(); if(class_exists('CURLFile')){//php 5.5 $post['pic1'] = new CURLFile(realpath($file)); } else { $post['pic1'] = '@'.realpath($file); } } else { $post['b64_data'] = base64_encode(file_get_contents($file)); } // echo $post['b64_data']; //Curl 提交 $ch = curl_init($url); curl_setopt_array($ch, array( CURLOPT_POST => true, CURLOPT_VERBOSE => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => array("Cookie: $cookie"), CURLOPT_POSTFIELDS => $post, )); $output = curl_exec($ch); curl_close($ch); // 正則表示式提取返回結果中的json資料 preg_match('/({.*)/i', $output, $match); if(!isset($match[1])) return ''; return $match[1]; } /** 通過今日詩詞API獲取詩詞內容 **/ function jinrishici(){ $opts = array( 'http'=>array( 'method'=>"GET", 'header'=>"Accept-language: en\r\n"."X-User-Token: k4z4CMgTyl3JN6s+y2iWWiHN6we+0J9V\r\n" ) ); $context = stream_context_create($opts); // Open the file using the HTTP headers set above $tangshi_pailie = json_decode(file_get_contents('https://v2.jinrishici.com/one.json', false, $context),true);//今日詩詞API,帶token版本 // $tangshi_pailie = json_decode(file_get_contents('https://v2.jinrishici.com/one.json'), true);//今日詩詞api,不帶token版本 $tangshi_title = $tangshi_pailie['data']['origin']['title'];//標題 $tangshi_dynasty = $tangshi_pailie['data']['origin']['dynasty'];//朝代 $tangshi_author = $tangshi_pailie['data']['origin']['author'];//詩人 $tangshi_line_numbers = count($tangshi_pailie['data']['origin']['content']); $tangshi_content = $tangshi_pailie['data']['origin']['content'][0]; for ($i=1; $i < $tangshi_line_numbers; $i++) { $tangshi_temp_line = $tangshi_pailie['data']['origin']['content'][$i]; $tangshi_content = $tangshi_content."\n".$tangshi_temp_line; }//拼接全詩 $post_Poem = "《".$tangshi_title."》"."\n".$tangshi_dynasty."·".$tangshi_author."\n"."\n".$tangshi_content; return "$post_Poem"; } include './wbcookie.php'; $cookie = $config['cookie']; //通過圖片api獲取圖片,並轉存微博圖床 $bing_img = json_decode(upload('https://www.yuluoge.com/api/index.php?cid=5', $cookie, false),true); $bing_img_pid = $bing_img['data']['pics']['pic_1']['pid']; echo "$bing_img_pid\n"; $tangshi = jinrishici(); echo "$tangshi\n"; $post=[ 'title' =>'今日要說什麼?', 'location' => 'v6_content_home', 'text' => "#詩詞[超話]# #中華好詩詞# #中國詩詞大會#"."\n".$tangshi."\n"."\n",//需要傳送微博的內容 'pic_id' =>"$bing_img_pid", // '007CcEyfly1g042kquhztj31ns0u0tdu',//微博圖片id,需事先上傳好 'isReEdit' => false, 'pub_source' => 'page_2', 'topic_id' => '1022%3A', 'pub_type' => 'dialog', '_t' => 0, 'style_type' => 1, ]; $url='https://weibo.com/aj/mblog/add?ajwvr=6&__rnd=2918942797035';//不需要改變 $referer='https://weibo.com/liufengshishe/home?topnav=1&wvr=6';//你的微博使用者名稱(首頁連結) $response = curl($url,$post,'',$cookie,$referer); echo "$response\n傳送成功";
微博登入模組
<?php if (!is_file('./wbcookie.php')) { CookieSet('SUB;','0'); } include './wbcookie.php'; require_once './weiboAccount.php'; if (time() - $config['time'] >20*3600||$config['cookie']=='SUB;') { $cookie = login($sinauser,$sinapwd); if($cookie&&$cookie!='SUB;') { CookieSet($cookie,$time = time()); } else { return error('203','獲取cookie出現錯誤,請檢查賬號狀態或者重新獲取cookie'); } } /** * 新浪微博登入(無加密介面版本) * @paramstring $u 使用者名稱 * @paramstring $p 密碼 * @return string返回最有用最精簡的cookie */ function login($u,$p){ $loginUrl = 'https://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.15)&_=1403138799543'; $loginData['entry'] = 'sso'; $loginData['gateway'] = '1'; $loginData['from'] = 'null'; $loginData['savestate'] = '30'; $loginData['useticket'] = '0'; $loginData['pagerefer'] = ''; $loginData['vsnf'] = '1'; $loginData['su'] = base64_encode($u); $loginData['service'] = 'sso'; $loginData['sp'] = $p; $loginData['sr'] = '1920*1080'; $loginData['encoding'] = 'UTF-8'; $loginData['cdult'] = '3'; $loginData['domain'] = 'sina.com.cn'; $loginData['prelt'] = '0'; $loginData['returntype'] = 'TEXT'; return loginPost($loginUrl,$loginData); } /** * 傳送微博登入請求 * @paramstring $url介面地址 * @paramarray$data 資料 * @return json算了,還是返回cookie吧//返回登入成功後的使用者資訊json */ function loginPost($url,$data){ $tmp = ''; if(is_array($data)){ foreach($data as $key =>$value){ $tmp .= $key."=".$value."&"; } $post = trim($tmp,"&"); }else{ $post = $data; } $ch = curl_init(); curl_setopt($ch,CURLOPT_URL,$url); curl_setopt($ch,CURLOPT_RETURNTRANSFER,1); curl_setopt($ch,CURLOPT_HEADER,1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch,CURLOPT_POST,1); curl_setopt($ch,CURLOPT_POSTFIELDS,$post); $return = curl_exec($ch); curl_close($ch); return 'SUB' . getSubstr($return,"Set-Cookie: SUB",'; ') . ';'; } /** * 取本文中間 */ function getSubstr($str,$leftStr,$rightStr){ $left = strpos($str, $leftStr); echo '左邊:'.$left; $right = strpos($str, $rightStr,$left); echo '<br>右邊:'.$right; if($left <= 0 or $right < $left) return ''; return substr($str, $left + strlen($leftStr), $right-$left-strlen($leftStr)); } /** 設定cookie檔案 */ function CookieSet($cookie,$time){ $newConfig = '<?php $config = array( "cookie" => "'.$cookie.'", "time" => "'.$time.'", );'; @file_put_contents('./wbcookie.php', $newConfig); } /** 錯誤反饋 */ function error($code,$msg){ $arr = array('code'=>$code,'msg'=>$msg); echo json_encode($arr); }
微博賬號模組
<?php $sinauser = '[email protected]';//你的微博賬號 $sinapwd = '123456789';//你的微博密碼
關於如何使用
本地搭建了lamp環境的話,開啟lamp環境後,直接在瀏覽器位址列輸入localhost及主入口檔案對應的路徑就可以運行了(本人使用xampp)。
雲伺服器的話,本人的方案是使用寶塔伺服器面板安裝lamp環境後,使用xftp將檔案傳到apache伺服器網站根目錄上,開啟lnmp環境就可以了的。
關於安全性問題
實不相瞞,如果是在雲伺服器上直接跑這些php檔案的話,是不太安全的。因為網站的公共使用者具有可以訪問微博賬號檔案的許可權。所以,推薦對微博賬號檔案進行.htaccess設定,也推薦申請個小號來搭建。
具體操作有點複雜,可以參考這篇文章—— 《apache .htaccess檔案詳解和配置技巧總結》
關於定時任務
設定定時任務的話可以使用linux主機的crontab命令。
- 遠端連線主機,連線成功後,輸入命令crontab -e;
- 會開啟一個檔案,按照格式輸入需要執行的指令碼;
- 儲存退出後,重啟crontab服務。
語法解釋:
“*” 代表取值範圍內的數字,
“/” 代表”每”,
“-” 代表從某個數字到某個數字,
“,” 分開幾個離散的數字
參考文獻
特別緻謝