
  Вступление 
Добро пожаловать в последний эпизод нашей серии Twitter API . В нашем последнем эпизоде я создал Twixxr.com, который позволит вам обнаружить влиятельных женщин в Твиттере для вашей учетной записи. Сегодня я собираюсь обратить внимание на своих последователей.
Хотя я не пользовался Facebook с 2013 года , я оставался активным в Твиттере — даже несмотря на то, что они загружали мой канал рекламой и раздражали меня, пытаясь алгоритмически оптимизировать его.
Недавно я был проверен и начал собирать подписчиков чуть быстрее. Я надеялся, что смогу увидеть больше ответов на мои твиты. Вообще, я был удивлен тем, как мало людей обычно получают отклик в Твиттере.

У меня почти 1900 подписчиков, но редко люди комментируют или ретвитят фрагменты, которые, на мой взгляд, важны и представляют общий интерес. Например, ни один человек не поделился моей статьей о резком всплеске сообщений об изнасилованиях в Сиэтле или комментариев о Билле Гейтсе в его наиболее вопиющей лицемерности .
В течение долгого времени я хотел более внимательно посмотреть на своих подписчиков в Твиттере и ответить на несколько вопросов: кто именно следит за мной? И почему они не более интерактивны? Возможно ли, что только 10% моих последователей являются настоящими людьми?
У Twitter проблемы с поиском покупателя, и, возможно, это как-то связано с этим.
API Twitter — хороший инструмент для исследования этого. Тем не менее, он имеет множество ограничений скорости, которые делают даже что-то простое, например, анализ ваших подписчиков, довольно сложным. В сегодняшнем эпизоде я покажу вам, как я работал с ограничениями скорости, чтобы оценить и построить табло моих подписчиков.
Если у вас есть какие-либо вопросы или комментарии, пожалуйста, оставьте их ниже в комментариях или свяжитесь со мной в Twitter @reifman .
Проанализируйте наших последователей в Твиттере

Чуть выше вы можете увидеть базовое табло, которое я создал. Сегодняшний эпизод будет посвящен в основном инфраструктуре и подходу, который я выбрал для создания этого. Я надеюсь, что у меня будет возможность написать больше об улучшении механизма оценки.
И да, как вы можете видеть выше, известный лидер прав геев и комментатор по сексуальным советам Дэн Сэвидж следует за мной, но никогда не ретвитит то, что я разделяю. Если сегодня будет время, мы проанализируем это, чтобы ответить на такие важные вопросы, как: он настоящий, бот или просто следует за мной за советом по поводу секса? Что мы можем узнать из его аккаунта, чтобы определить, будет ли он когда-либо взаимодействовать со мной в Твиттере или, если уж на то пошло, с кем-либо из моих последователей?
Код на табло — это, в основном, прототип, который я построил поверх кода Twixxr из последнего эпизода, но это не живая демонстрация для людей. Я делюсь, чтобы вы могли учиться на этом и строить на нем самостоятельно.
Вот основные элементы кода:
- Создание базы данных для хранения моих подписчиков и связанных с ними данных.
- Загрузка моих подписчиков на страницах по 20 подписчиков каждая.
- Отслеживание курсоров для страниц, когда я загружаю 15 страниц в окне с ограниченной скоростью.
- Хранение собранных данных о моих подписчиках в базе данных.
- Построение алгоритма оценки прототипа для оценки всех последователей.
- Построение представления для просмотра табло.
Погружение в кодекс
Создание таблиц базы данных Миграции
Я создал три разных таблицы, чтобы хранить все данные и помогать мне работать с ограничением скорости Twitter API. Если вы не знакомы с миграциями баз данных Yii, см. Раздел «Как программировать с помощью Yii2: работа с базой данных и активной записью» .
Во-первых, я расширил таблицу SocialProfile, чтобы записать намного больше данных из учетных записей подписчиков, например, были ли они проверены, их местоположение и сколько объектов они выбрали:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?php use yii\db\Schema; use yii\db\Migration; class m161026_221130_extend_social_profile_table extends Migration {         public function up()         {           $tableOptions = null;           if ($this->db->driverName === ‘mysql’) {               $tableOptions = ‘CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB’;           }           $this->addColumn(‘{{%social_profile}}’,’social_id’,Schema::TYPE_STRING.’ NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’name’,’string NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’screen_name’,Schema::TYPE_STRING.’ NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’description’,Schema::TYPE_TEXT.’ NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’url’,Schema::TYPE_STRING.’ NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’protected’,Schema::TYPE_SMALLINT. ‘ NOT NULL DEFAULT 0’);           $this->addColumn(‘{{%social_profile}}’,’favourites_count’,Schema::TYPE_BIGINT. ‘ NOT NULL DEFAULT 0’);           $this->addColumn(‘{{%social_profile}}’,’verified’,Schema::TYPE_SMALLINT. ‘ NOT NULL DEFAULT 0’);           $this->addColumn(‘{{%social_profile}}’,’location’,Schema::TYPE_STRING.’ NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’profile_location’,Schema::TYPE_STRING.’ NOT NULL’);           $this->addColumn(‘{{%social_profile}}’,’score’,Schema::TYPE_BIGINT. ‘ NOT NULL DEFAULT 0’);         } | 
 Затем я создал таблицу индексации SocialFriend для отслеживания подписчиков для определенных учетных записей.  Если я решу официально оформить эту услугу, она мне понадобится.  Он связывает таблицу User с подписчиками пользователя в таблице SocialProfile. 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | <?php   use yii\db\Schema;   use yii\db\Migration; class m161026_233916_create_social_friend_table extends Migration {    public function up()    {        $tableOptions = null;        if ($this->db->driverName === ‘mysql’) {            $tableOptions = ‘CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB’;        }        $this->createTable(‘{{%social_friend}}’, [            ‘id’ => Schema::TYPE_PK,            ‘user_id’ => Schema::TYPE_BIGINT.’            ‘social_profile_id’ => Schema::TYPE_BIGINT.’        ], $tableOptions);    } | 
Далее, API Twitter требует, чтобы вы просматривали запросы 20 подписчиков одновременно. Чтобы узнать следующую страницу, вы должны отслеживать курсоры, в основном теги, которые отмечают следующую страницу для извлечения.
  Поскольку вам разрешено делать только 15 запросов на подписчиков каждые 15 минут, вы должны хранить эти курсоры в базе данных.  Таблица называется SocialCursor : 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | <?php   use yii\db\Schema;   use yii\db\Migration;   class m161027_001026_social_cursor_table extends Migration {    public function up()    {        $tableOptions = null;        if ($this->db->driverName === ‘mysql’) {            $tableOptions = ‘CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB’;        }        $this->createTable(‘{{%social_cursor}}’, [            ‘id’ => Schema::TYPE_PK,            ‘user_id’ => Schema::TYPE_BIGINT.’            ‘next_cursor’ => Schema::TYPE_STRING.’        ], $tableOptions);    } | 
В конце концов, я буду создавать фоновые задачи cron для управления всем этим, но для сегодняшнего прототипа я запускаю эти задачи вручную.
Сбор подписчиков и их учетных данных
  Затем я создал метод Twitter::getFollowers() для выполнения запроса.  Вот основы кода: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public function getFollowers($user_id) {  $sp = new SocialProfile();  $next_cursor = SocialCursor::getCursor($user_id);  …  while ($next_cursor>0) {      $followers = $this->connection->get(«followers/list»,[‘cursor’=>$next_cursor]);      if ($this->connection->getLastHttpCode() != 200) {        var_dump($this->connection);        exit;      }        if (isset($followers->users)) {          foreach ($followers->users as $u) {            $n+=1;            $users[]=$u;            $sp->add($user_id,$u);          }        $next_cursor= $followers->next_cursor;        SocialCursor::refreshCursor($user_id,$next_cursor);        echo $next_cursor.'<br />’;        echo ‘======================================================<br />’;      } else {        exit;      }  } | 
  Он получает next_cursor и неоднократно запрашивает последователей , $followers = $this->connection->get("followers/list",['cursor'=>$next_cursor]) , пока не достигнет ограничения скорости. 
Вывод выглядит примерно так, как он проходит через каждую страницу из 20 результатов:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | refresh cursor: 1489380833827620370 ====================================================== refresh cursor: 1488086367811119559 ====================================================== refresh cursor: 1486452899268510188 ====================================================== refresh cursor: 1485593015909209633 ====================================================== refresh cursor: 1485330282069552137 ====================================================== refresh cursor: 1485256983607000799 ====================================================== refresh cursor: 1484594012550322889 ====================================================== refresh cursor: 1483359799854574028 ====================================================== refresh cursor: 1481615590678791493 ====================================================== refresh cursor: 1478424827838161031 ====================================================== refresh cursor: 1477449626282716582 ====================================================== refresh cursor: 1475751176809638917 ====================================================== refresh cursor: 1473539961706830585 ====================================================== refresh cursor: 1471375035531579849 ====================================================== | 
  Данные сохраняются этими $sp->add($user_id,$u);  методы.  Метод SocialProfile::add() является другой версией метода fill() из учебника Twixxr.  Он хранит больше данных и управляет индексом SocialFriend: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | public static function add($user_id,$profileObject=null) {      $sp = SocialProfile::find()        ->where([‘social_id’=>$profileObject->id_str])        ->one();      if (!isset($profileObject->name) || empty($profileObject->name)) {        $profileObject->name=’Nameless’;      }      if (!isset($profileObject->url) || empty($profileObject->url)) {        $profileObject->url=»;      }      if (!isset($profileObject->screen_name) || empty($profileObject->screen_name)) {        $profileObject->screen_name=’error_sn’;      }      if (!isset($profileObject->description) || empty($profileObject->description)) {        $profileObject->description='(empty)’;      }      if (!isset($profileObject->profile_location) || empty($profileObject->profile_location)) {        $profileObject->profile_location=»;      }      if (!isset($profileObject->profile_image_url_https) || empty($profileObject->profile_image_url_https)) {        $profileObject->profile_image_url_https=»;      }      if (!is_null($sp)) {        $sp->social_id = $profileObject->id;        $sp->image_url = $profileObject->profile_image_url_https;        $sp->follower_count= $profileObject->followers_count;        $sp->status_count = $profileObject->statuses_count;        $sp->friend_count = $profileObject->friends_count;        $sp->listed_in = $profileObject->listed_count;        $sp->url=$profileObject->url;        if ($profileObject->protected) {            $sp->protected=1;        } else {          $sp->protected=0;        }        if ($profileObject->verified) {            $sp->verified=1;        } else {          $sp->verified=0;        }        $sp->favourites_count=$profileObject->favourites_count;        $sp->location=$profileObject->location;        $sp->profile_location=$profileObject->profile_location;        $sp->name = $profileObject->name;        $sp->description = $profileObject->description;        $sp->image_url = $profileObject->profile_image_url_https;        if ($sp->validate()) {            $sp->update();        } else {          var_dump($sp->getErrors());        }      } else {        $sp = new SocialProfile();        $sp->social_id = $profileObject->id;        $sp->score = 0;        $sp->header_url=»;        $sp->url=$profileObject->url;        $sp->favourites_count=$profileObject->favourites_count;        if ($profileObject->protected) {            $sp->protected=1;        } else {          $sp->protected=0;        }        if ($profileObject->verified) {            $sp->verified=1;        } else {          $sp->verified=0;        } $sp->location=$profileObject->location;        $sp->profile_location=$profileObject->profile_location;        $sp->name = $profileObject->name;        $sp->description = $profileObject->description;        $sp->screen_name = $profileObject->screen_name;        $sp->image_url = $profileObject->profile_image_url_https;        $sp->follower_count= $profileObject->followers_count;        $sp->status_count = $profileObject->statuses_count;        $sp->friend_count = $profileObject->friends_count;        $sp->listed_in = $profileObject->listed_count;        if ($sp->validate()) {            $sp->save();        } else {          var_dump($sp->getErrors());        }      }      $sf = SocialFriend::find()        ->where([‘social_profile_id’=>$sp->id])        ->andWhere([‘user_id’=>$user_id])        ->one();      if (is_null($sf)) {          $sf = new SocialFriend();          $sf->user_id = $user_id;          $sf->social_profile_id = $sp->id;          $sf->save();      }      return $sp->id;    } | 
Он написан для сохранения новых записей или обновления старых записей, чтобы в будущем вы могли отслеживать данные подписчика и регулярно обновлять его, перезаписывая старые данные.
Этот последний раздел в конце обеспечивает наличие индекса SocialFriend между таблицей User и таблицей SocialProfile.
| 01 02 03 04 05 06 07 08 09 10 | $sf = SocialFriend::find()        ->where([‘social_profile_id’=>$sp->id])        ->andWhere([‘user_id’=>$user_id])        ->one();      if (is_null($sf)) {          $sf = new SocialFriend();          $sf->user_id = $user_id;          $sf->social_profile_id = $sp->id;          $sf->save();      } | 
Подсчет очков в Твиттере

У меня было несколько голов для оценки в Твиттере:
- Удалите аккаунты, которые следуют за всеми, кто за ними следует Например, у них есть 12 548 подписчиков и подписаны на 12 392 человека (см. Выше).
- Удалите учетные записи после более чем 1500 учетных записей, которые вряд ли когда-либо увидят то, чем я делюсь. Например, Дэн Сэвидж следует 1536 человек.
- Удалите аккаунты, у которых очень мало постов или очень мало аккаунтов, за которыми они следуют, вероятно, заброшенные аккаунты
- Удалите учетные записи с помощью нескольких избранных — это скорее всего боты, которые на самом деле не используют приложение.
Точно так же я хотел бы выделить некоторые положительные моменты:
- Счета, которые проверены
- Аккаунты с большим количеством подписчиков
- Аккаунты, на которых подписаны менее 1000 человек — приятное место для меня
  Вот примерный базовый код из SocialProfile::score() который выделяет некоторые из положительных моментов: 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | foreach ($all as $sp) {        // score sp        $score =0;        // RULE IN        if ($sp->verified==1) {          $score+=1000;        }        // POSITIVE        if ($sp->protected==1) {          $score+=500;        }        if ($sp->follower_count > 10000) {          $score+=500;        } else if ($sp->follower_count > 3500) {          $score+=750;        } else if ($sp->follower_count > 1100) {          $score+=1000;        } else if ($sp->follower_count > 1000) {          $score+=250;        } else if ($sp->follower_count> 500) {          $score+=250;        } | 
Вот некоторый код, который устраняет некоторые плохие учетные записи:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // RULE OUT        // make this a percentage of magnitude        $magnitude = $sp->follower_count/1000;        if ($sp->follower_count> 1000 and abs($sp->follower_count-$sp->friend_count)<$magnitude) {          $score-=2500;        }        if ($sp->friend_count > 7500) {            $score-=10000;        } else        if ($sp->friend_count > 5000) {            $score-=5000;        }         else if ($sp->friend_count > 2500) {            $score-=2500;        }else if ($sp->friend_count > 2000) {           $score-=2000;       } else if ($sp->friend_count > 1000) {            $score-=250;        } else if ($sp->friend_count > 750) {          $score-=100;        }        if ($sp->follower_count<100) {          $score-=1000;        }        if ($sp->status_count < 35) {          $score-=5000;        } | 
Очевидно, здесь есть над чем поиграть, и есть множество способов улучшить это. Я надеюсь, что у меня будет возможность потратить больше времени на это.
Когда метод запускается, он выглядит следующим образом, но обновляет таблицу SocialProfile с оценками:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | DJMany -6300 gai_ltau -7850 Michal92B -900 InvestmentAdvsr -2900 TSSStweets -7500 sandcageapp -1750 dominicpouzin 1950 daletdykaaolch1 -7850 suzamack -8250 writingthrulife -7500 ryvr -1550 RichardAngwin -8300 DanielleMorrill -7300 ReversaCreates 2750 BoKnowsMarkting -7500 TheHMProA -8500 HouseMgmt101 750 itsmeKennethG -1250 drbobbiwegner -8500 Mizzfit_Bianca -7300 wilsonmar 700 CoachVibeke -7300 jhurwitz 0 PiedPiperComms 500 Prana2thePeople -1100 singlemomspower -2250 mouselink -7300 MotivatedGenY -7300 brett7three -7300 JovanWalker 2950 ITSPmagazine 450 RL_Miller -2250 | 
Отображение Табло
Сетка Yii по умолчанию позволяет довольно легко отображать таблицу SocialProfile и настраивать столбцы табло.
  Вот SocialProfileController::actionIndex() : 
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 | /**     * Lists all SocialProfile models.     * @return mixed     */    public function actionIndex()    {        $searchModel = new SocialProfileSearch();        $dataProvider = $searchModel->search(Yii::$app->request->queryParams);        return $this->render(‘index’, [            ‘searchModel’ => $searchModel,            ‘dataProvider’ => $dataProvider,        ]);    } | 
И вот настроенный вид сетки:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <?php use yii\helpers\Html; use yii\grid\GridView; use yii\widgets\Pjax; /* @var $this yii\web\View */ /* @var $searchModel frontend\models\SocialProfileSearch */ /* @var $dataProvider yii\data\ActiveDataProvider */ $this->title = Yii::t(‘frontend’, ‘Social Profiles’); $this->params[‘breadcrumbs’][] = $this->title; ?> <div class=»social-profile-index»>     <h1><?= Html::encode($this->title) ?></h1>     <?php // echo $this->render(‘_search’, [‘model’ => $searchModel]); <?php Pjax::begin();         ‘dataProvider’ => $dataProvider,         ‘filterModel’ => $searchModel,         ‘columns’ => [             [‘class’ => ‘yii\grid\SerialColumn’],             [             ‘label’=>’Name’,               ‘format’ => ‘raw’,               ‘value’ => function ($model) {                       return ‘<div><span><strong><a href=»http://twitter.com/’.$model->screen_name.’»>’.$model->name.'</a></strong><br />’.$model->screen_name.’                   },           ],             ‘score’,             [             ‘label’=>’Follows’,               ‘format’ => ‘raw’,               ‘attribute’=>’friend_count’,             ],             [             ‘label’=>’Followers’,               ‘format’ => ‘raw’,               ‘attribute’=>’follower_count’,             ],             [             ‘label’=>’Tweets’,               ‘format’ => ‘raw’,               ‘attribute’=>’status_count’,             ],             [             ‘label’=>’Favs’,               ‘format’ => ‘raw’,               ‘attribute’=>’favourites_count’,             ],             [             ‘label’=>’Listed’,               ‘format’ => ‘raw’,               ‘attribute’=>’listed_in’,             ],             [             ‘label’=>’P’,               ‘format’ => ‘raw’,               ‘attribute’=>’protected’,             ],           [           ‘label’=>’V’,             ‘format’ => ‘raw’,             ‘attribute’=>’verified’,         ],             // ‘location’,             // ‘profile_location’,             [             //’contentOptions’ => [‘class’ => ‘col-lg-11 col-xs-10’],             ‘label’=>’Pic’,               ‘format’ => ‘raw’,               ‘value’ => function ($model) {                       return ‘<div><span><img src=»‘.$model->image_url.’»>                   },           ],         ],     ]); <?php Pjax::end(); | 
Вот как выглядят лучшие результаты моего первоначального алгоритма:

Есть так много способов улучшить и настроить выигрыш. Я с нетерпением жду возможности поиграть с этим больше.
И еще я бы хотел написать код и расширить использование API, например:
- Используйте гендер PHP, чтобы помочь устранить компании из людей (компании не очень много взаимодействуют).
- Посмотрите частоту постов, которые люди делали, и последний раз, когда они использовали Twitter.
- Используйте API поиска в Твиттере, чтобы увидеть, какие подписчики когда-либо взаимодействовали с моим контентом.
- Обеспечить обратную связь с оценкой, чтобы настроить его.
  Заглядывая вперед 
Я надеюсь, что вы находите подход к выигрышу интригующим. Можно сделать гораздо больше, чтобы улучшить это. Пожалуйста, не стесняйтесь играть с ним и опубликуйте свои идеи ниже.
Если у вас есть какие-либо вопросы или предложения, пожалуйста, оставьте их в комментариях. Если вы хотите быть в курсе моих будущих уроков Envato Tuts + и других серий, пожалуйста, посетите мою страницу инструктора или следуйте @reifman . Обязательно посмотрите мою серию стартапов и Планировщик собраний .
Ссылки по теме
- Twixxr (недавний пример приложения Twitter API для обнаружения влиятельных женщин)
- Твиттер Документация для разработчиков
- Как программировать с помощью серии Yii2 (Envato Tuts +)