藉助詩詞API和微博圖牀搭建自動發圖文微博機器人

​ 在2011年的時候,浙大的一位博士生藉助微博的開放平臺爲他實驗室的一臺飲水機弄了個微博,名喚@浙大CCNT實驗室飲水機,俗稱「飲水機娘「。當年這條新聞給本身留下了挺大的印象,也一直對這個微博帳號能夠自動發微博背後的機理感到十分憧憬。一晃,時間都來到了2019年了~~php

​ 咳咳——在鑽研很多技術文章以及對技術派網友的多多請教後,本身終於也搗弄了一個自動發微博的機器人( ̄︶ ̄)↗html

​ 此微博機器人的功能以下:linux

  • 直接模擬登錄新浪微博;
  • 自動獲取唐詩宋詞文本;
  • 自動獲取文藝主題圖片;
  • 自動上傳圖片至微博圖牀;
  • 自動發送內容不一樣的圖文微博;
  • 經過定時任務,實現週期性發微博任務。

​ 實際效果圖:git

​ GitHub倉庫:github

https://github.com/Leslie-Won...

​ 正所謂「前不見古人,後不見來者。念天地之悠悠,獨愴然而涕下!「,咳咳——IT技術世界固然不是這樣,咱們的技術積累都是站在前人的基礎上的,換言之,站在巨人的肩膀上。因此,仍是先來囉嗦囉嗦當年的飲水機娘。正則表達式

飲水機娘分析

​ 當年果殼網在飲水機娘爆紅了的時候,採訪了背後的開發者——浙江大學計算機科學與技術學院的一位陳姓博士生。文章標題是《揭開「飲水機娘」的神祕面紗》。在這篇文章中,闡述發微博原理的段落以下:算法

據陳同窗介紹,飲水機自己並無多加改造,只是飲水機上安裝一個攝像頭,鏡頭正對加熱指示燈,做爲傳感器,實時監控加熱狀態。

發送微博的功能經過代碼實現,利用了新浪微博開放平臺提供的PHP語言軟件開發工具包。在代碼的設計中,主要有檢測模塊和反應模塊兩部分。檢測模塊處理攝像頭的監控數據,捕捉加熱指示燈「亮->不亮」與「不亮->亮」兩個切換狀態,而後調用反應模塊及時發送微博。因此在「飲水機娘」自動發送的微博下方,會顯示「來自未經過審覈應用」。目前,完成這些功能,所需的代碼量不足兩百行。apache

​ 如今來分析分析這兩段話,把整個流程弄成流程圖的話是以下的效果:json

​ 從「指示燈」到「視覺算法判斷狀態」這部分屬於計算機視覺實現了,依本人目前的技術視野判斷,能夠藉助openCV來構建。至於發送微博這一部分,則是純粹的PHP代碼實現。因爲本文所要討論的是構建一個發微博的機器人,而微博報文數據的獲取能夠有不少種方式,所以,openCV就點到爲止了。(本身也不是太懂openCV)(。・_・)/~~~api

​ 本身在查閱了很多技術文獻後,經過這篇《新浪微博自動(模擬)登錄詳解及實現》瞭解到飲水機娘發送的微博下方會出現「來自未經過審覈應用」是因爲用了新浪微博開放平臺的接口的緣故,並且其會有幾個比較致命的限制(調用次數限制和受權期限限制)。網上流傳一種直接模擬登錄微博的解決方案,關鍵點就是利用php的curl功能,這也是本人所要闡述的微博機器人使用的登陸原理。

​ 另外,翻了翻飲水機娘最先期發送的微博,報告飲水機水沸騰了的微博報文是這樣子的——

1552649398204

​ 後來變成了這樣子——

​ 而對應「亮->不亮」狀態的微博報文最初是這樣子的——

​ 不過,後來關注度上去以後,就很難判斷飲水機娘發送的微博是否是根據飲水機狀態自動發出去了的了,可是也不影響本文後續的敘述。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命令。

  1. 遠程鏈接主機,鏈接成功後,輸入命令crontab -e;
  2. 會打開一個文件,按照格式輸入須要執行的腳本;
  3. 保存退出後,重啓crontab服務。

語法解釋:

「*」 表明取值範圍內的數字,
「/」 表明」每」,
「-」 表明從某個數字到某個數字,
「,」 分開幾個離散的數字

參考文獻

《揭開「飲水機娘」的神祕面紗》

《新浪微博自動(模擬)登錄詳解及實現》

《今日詩詞開放接口-調用文檔》

《利用微博當圖牀-php語言實現》

《apache .htaccess文件詳解和配置技巧總結》

《linux下crontab定時訪問指定url》


特別緻謝

九凌少子

相關文章
相關標籤/搜索