在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/i...
不同的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.weib...
本文所構建機器人略有改動地使用了這篇文章裏的獲取新浪圖牀圖片pid的PHP源碼。源碼如下:
/**
* 上傳圖片到微博圖牀
* @author mengkun http://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');
}
}
/**
* 新浪微博登錄(無加密接口版本)
* @param string $u 用户名
* @param string $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);
}
/**
* 發送微博登錄請求
* @param string $url 接口地址
* @param array $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 = 'example@email.com';//你的微博賬號
$sinapwd = '123456789';//你的微博密碼
關於如何使用
本地搭建了lamp環境的話,開啓lamp環境後,直接在瀏覽器地址欄輸入localhost及主入口文件對應的路徑就可以運行了(本人使用xampp)。
雲服務器的話,本人的方案是使用寶塔服務器面板安裝lamp環境後,使用xftp將文件傳到apache服務器網站根目錄上,開啓lamp環境就可以了的。
關於安全性問題
實不相瞞,如果是在雲服務器上直接跑這些php文件的話,是不太安全的。因為網站的公共用户具有可以訪問微博賬號文件的權限。所以,推薦對微博賬號文件進行.htaccess設置,也推薦申請個小號來搭建。
具體操作有點複雜,可以參考這篇文章——《apache .htaccess文件詳解和配置技巧總結》
關於定時任務
設置定時任務的話可以使用linux主機的crontab命令。
- 遠程連接主機,連接成功後,輸入命令crontab -e;
- 會打開一個文件,按照格式輸入需要執行的腳本;
- 保存退出後,重啓crontab服務。
語法解釋:
“*” 代表取值範圍內的數字,
“/” 代表”每”,
“-” 代表從某個數字到某個數字,
“,” 分開幾個離散的數字
參考文獻
《揭開“飲水機娘”的神秘面紗》《新浪微博自動(模擬)登陸詳解及實現》
《今日詩詞開放接口-調用文檔》
《利用微博當圖牀-php語言實現》
《apache .htaccess文件詳解和配置技巧總結》
《linux下crontab定時訪問指定url》
特別緻謝
九凌少子