使用cURL,PHP和Twitter而不使用API​​的逻辑 [英] Logic using cURL, PHP and Twitter without using API

查看:59
本文介绍了使用cURL,PHP和Twitter而不使用API​​的逻辑的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有此代码,它将 cookies 保存在 .txt 文件中,并通过Twitter验证用户身份

I have this code, which saves the cookies in a .txt file, and authenticates the user with Twitter

<?php

require_once 'class/Cookies.php';

$cookie = [];

$username = 'formUser';
$password = 'formPass';

$index_url = 'https://twitter.com';

$token = curl_init();
curl_setopt_array($token, [
      CURLOPT_URL             => $index_url,
      CURLOPT_CUSTOMREQUEST   => 'GET',
      CURLOPT_RETURNTRANSFER  => true,
      CURLOPT_SSL_VERIFYPEER  => false,
      CURLOPT_SSL_VERIFYHOST  => 2,
      CURLOPT_USERAGENT       => $_SERVER['HTTP_USER_AGENT'],
      //CURLOPT_COOKIEFILE      => __DIR__ . DIRECTORY_SEPARATOR . 'cookies' . DIRECTORY_SEPARATOR . $username . '.txt',
      CURLOPT_COOKIEJAR       => __DIR__ . DIRECTORY_SEPARATOR . 'cookies' . DIRECTORY_SEPARATOR . $username . '.txt',
      CURLOPT_COOKIESESSION   => true,
      CURLOPT_REFERER         => $index_url,
      CURLOPT_HEADER          => true,
      CURLOPT_HTTPHEADER      => ['Cookie:' . http_build_query($cookie, '', ';') . ';'],
      CURLOPT_HEADERFUNCTION => function ($curl, $header) use (&$cookie) {
        if (stripos($header, 'Set-Cookie:') === 0) {
          if (preg_match('/Set-Cookie:\s?(.*?)=(.*?);/i', $header, $matches)) {
            $cookie[$matches[1]] = urldecode($matches[2]);
          }
        }
        return strlen($header);
      }
    ]
);    
$access = curl_exec($token);

preg_match('/value="(.*?)" name="authenticity_token"/', $access, $matches);

$authenticity_token = $matches[1];

//var_dump($authenticity_token);

$session_post = "session[username_or_email]=$username&session[password]=$password&return_to_ssl=true&scribe_log=&redirect_after_login=%2F&authenticity_token=$authenticity_token";



$session_url = 'https://twitter.com/sessions';

curl_setopt_array($token, [
      CURLOPT_URL             => $session_url,
      CURLOPT_CUSTOMREQUEST   => 'POST',
      CURLOPT_POSTFIELDS      => $session_post,
      CURLOPT_RETURNTRANSFER  => true,
      CURLOPT_HTTPHEADER      => [
        "Content-type: application/x-www-form-urlencoded",
        'Cookie: '. http_build_query($cookie, '', ';').';'
      ],
      CURLOPT_USERAGENT       => $_SERVER['HTTP_USER_AGENT'],
      CURLOPT_HEADER          => true,
      CURLOPT_FOLLOWLOCATION  => true,
      CURLOPT_MAXREDIRS       => 2,
      CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
      CURLOPT_POSTREDIR       => 2,
      CURLOPT_AUTOREFERER     => 1
  ]

);
$auth = curl_exec($token);

var_dump($cookie);

if (isset($cookie['auth_token']))
{
  $twid = filter_var($cookie['twid'], FILTER_SANITIZE_NUMBER_INT);

  Cookies::set('login_token', $cookie['ct0']);
  Cookies::set('kdt', $cookie['kdt']);
  Cookies::set('user_id', $twid);
  Cookies::set('auth_token', $cookie['auth_token']);
  Cookies::set('username', $username);


  echo json_encode(array(
    "status"      => "success",
    "message"     => "Authentication successful, we are redirecting you.",
  ));
}
else
{
  echo json_encode(
    array(
      "status" => "error",
      'message'=> "Unable to authenticate with Twitter.",
    ));
}

此代码可捕获已登录用户的信息:

And this code that captures the information of the logged in user:

<?php

$username = 'sessionUser';

$url = 'https://twitter.com/' . $username;

$user = curl_init();
curl_setopt_array($user, [
      CURLOPT_URL             => $url,
      CURLOPT_CUSTOMREQUEST   => 'GET',
      CURLOPT_CAINFO          => 'cacert-2017-06-07.pem',
      CURLOPT_RETURNTRANSFER  => true,
      CURLOPT_SSL_VERIFYPEER  => false,
      CURLOPT_SSL_VERIFYHOST  => 2,
      CURLOPT_HTTPHEADER      => [
        "Content-type:text/html;charset=utf-8",
      ],
      CURLOPT_USERAGENT       => $_SERVER['HTTP_USER_AGENT'],
      CURLOPT_HEADER          => true,
      CURLOPT_FOLLOWLOCATION  => true,
      CURLOPT_MAXREDIRS       => 2,
      CURLOPT_REDIR_PROTOCOLS => CURLPROTO_HTTP | CURLPROTO_HTTPS,
      CURLOPT_POSTREDIR       => 2,
      CURLOPT_AUTOREFERER     => 1,
      CURLOPT_ENCODING        => "gzip"
  ]
);

$user_info = curl_exec($user);

$header_size = curl_getinfo($user, CURLINFO_HEADER_SIZE);
$header = substr($user_info, 0, $header_size);
$body = substr($user_info, $header_size);

$dom = new DOMDocument("5.0", "utf-8");
@$dom->loadHTML($body);

$data = json_decode($dom->getElementById("init-data")->getAttribute("value"));

echo "Nome: ", $data->profile_user->id, PHP_EOL;
echo "Nome: ", $data->profile_user->name, PHP_EOL;
echo "Usuário: ", $data->profile_user->screen_name, PHP_EOL;
echo "Foto de perfil: ", $data->profile_user->profile_image_url, PHP_EOL;

我需要帮助,以便保存在.txt或数据库中的用户的Cookie可以使关注者进行交流.

I need help so that the cookies of users saved in the .txt or in the database, make followers exchange.

我该怎么做?

编辑

投票否决的人,发表评论.

Who downvote, leave comments.

编辑2

文件 follow.php

<?php

require_once '../modules/config.php';
require_once '../modules/class/Cookies.php';


$username = Cookies::get('username');

$friend_url = 'https://api.twitter.com/1.1/friendships/create.json';

$friend = curl_init();

curl_setopt_array($friend, [
        CURLOPT_URL             => $friend_url,
        CURLOPT_SSL_VERIFYPEER  => 1,
        CURLOPT_SSL_VERIFYHOST  => 2,
        CURLOPT_CAINFO          => ROOT . 'modules' . SEPARATOR . 'cacert' . SEPARATOR . 'cacert-2017-06-07.pem',
        CURLOPT_CUSTOMREQUEST   => 'POST',
        CURLOPT_POSTFIELDS      => 'screen_name=' . $username,
        CURLOPT_USERAGENT       => $_SERVER['HTTP_USER_AGENT'],
        CURLOPT_RETURNTRANSFER  => true,
        CURLOPT_HTTPHEADER      => [
            "Content-type: application/json; charset=utf-8",
      ],
        CURLOPT_HEADER          => true,
    ]

);

$response  = curl_exec($friend);

var_dump($response);

响应:

C:\wamp64\www\brfollow\api\follow.php:32:string 'HTTP/1.1 400 Bad Request
content-length: 62
content-type: application/json; charset=utf-8
date: Fri, 07 Jul 2017 08:09:54 GMT
server: tsa_d
set-cookie: guest_id=v1%3A149941499419523606; Domain=.twitter.com; Path=/; Expires=Sun, 07-Jul-2019 08:09:54 UTC
strict-transport-security: max-age=631138519
x-connection-hash: 9e951d1215095efa246c5b852acd2e8a
x-response-time: 131
x-tsa-request-body-time: 0

{"errors":[{"code":215,"message":"Bad Authentication data."}]}' (length=472)

推荐答案

关于现有代码的前几条注意事项:不要对 GET POST 请求使用CURLOPT_CUSTOMREQUEST.对于 GET ,请使用 CURLOPT_HTTPGET =&true; true (还请注意,GET是libcurl的默认请求),对于 POST 请求,请使用 CURLOPT_POST => true .

first some notes on your existing code: don't use CURLOPT_CUSTOMREQUEST for GET and POST requests. for GET, use CURLOPT_HTTPGET=>true (also note that GET is the default request for libcurl), and for POST requests, use CURLOPT_POST=>true.

此行 preg_match('/value =(.*?)" name ="authenticity_token"/',$ access,$ matches); 如果他们在值和名称之间放置任何其他属性,则将中断,如果他们只是将名称移到值后面,它甚至会中断,并且会中断如果他们在注释中添加一个类似的字符串(<!-->> -style),并且如果他们在值和名称之间放置另一个空格,甚至会中断,并使用正则表达式解析HTML通常不是一个好主意

this line preg_match('/value="(.*?)" name="authenticity_token"/', $access, $matches); will break if they put any additional properties between the value and the name, and it will even break if they just move the name behind the value, and it will break if they put a simmilar string in a comment (<!-- -->-style), and it will even break if they just put another space between value and name, and parsing HTML with regex is generally a bad idea

一种更可靠的方法是:

$authenticity_token=(new DOMXpath(@DOMDocument::loadHTML($access)))->query("//input[@name='authenticity_token']")->item(0)->getAttribute("value");

在此行中,您犯了3次相同的错误:

in this line, you do the same mistake 3 times:

$session_post = "session[username_or_email]=$username&session[password]=$password&return_to_ssl=true&scribe_log=&redirect_after_login=%2F&authenticity_token=$authenticity_token";

您不会对$ username,$ password和$ authenticity_token进行urlencode.这意味着,如果这3个字符中的任何一个包含任何具有 application/x-www-urlencoded 格式的特殊含义的字符,则服务器将获得错误的数据(包括空格,& = [ÆØÅ和一个许多其他字符),简单的解决方案是对它们使用urlencode()-漂亮的解决方案是使用http_build_query生成字符串,如下所示:

you don't urlencode $username , $password, and $authenticity_token . that means, if any of those 3 contains any characters with special meaning in application/x-www-urlencoded format, the server will get the wrong data (this includes spaces, &, =, [,Æ,Ø,Å, and a lot of other characters), the easy solution is to use urlencode() on them - and the pretty solution is to use http_build_query to make the string, like this:

$session_post = http_build_query ( array (
        'session' => array (
                'username_or_email' => $username,
                'password' => $password 
        ),
        'return_to_sssl' => true,
        'scribe_log' => '',
        'redirect_after_login' => '/',
        'authenticity_token' => $authenticity_token 
) );

您在此行上也会犯同样的错误:

also you make the same mistake on this line:

    CURLOPT_POSTFIELDS      => 'screen_name=' . $username,

并且此行必须是错误添加的:

and this line must have been added by mistake:

  CURLOPT_HTTPHEADER      => [
    "Content-type:text/html;charset=utf-8",
  ],

这是一个没有请求主体的GET请求,因此没有 content-type ,因为没有内容,因此不可能存在content-type标头声明,摆脱它.

it is a GET request with no request body, thus there is no content-type, because there is no content, so there's no way that content-type header declaration is supposed to be there, get rid of it.

此行

  CURLOPT_ENCODING        => "gzip"

如果没有使用gzip编译curl,

将破坏您的代码,并且服务器实际上决定使用gzip(更具体地说,您将获得难以理解的二进制数据),并且您未提供任何代码来实际处理gzip二进制数据.一种更强大的方法是将其设置为emptystring " ,然后curl将提供libcurl编译后将提供的所有编码,并会即时为您解编码(包括gzip,如果已编译)in.通常是 gzip deflate ,但它也是面向未来的,因为它将自动添加任何将来的编码)

will break your code if curl was not compiled with gzip, and the server actually decide to use gzip (more specifically, you will get unintelligible binary data), and you provide no code to actually handle gzip binary data. a much more robust approach is to set it to emptystring "", then curl will provide all encodings that libcurl was compiled will, and will de-encode it for you on the fly (including gzip, if compiled in. its usually gzip and deflate, but its also future-proof because it will add any future encodings automatically)

此行

    "Content-type: application/x-www-form-urlencoded",

不要自动添加此标头.libcurl将自动检测 application/x-www-urlencoded multipart/form-data 编码,并自动设置适当的 content-type 标头.而且与您不同的是,libcurl不会打错字.

don't add this header automatically. libcurl will automatically detect application/x-www-urlencoded and multipart/form-data encodings, and set the appropriate content-type headers automatically. and unlike you, libcurl won't make typos in doing so.

现在,下一步是获取您当前的所有关注者,并发送关注请求.您说您不想使用该api,但是根本无法避免使用该API(缺少对Twitter数据库的攻击,ofc),即使是tiwtter的javascript的关注"按钮也使用该api.好消息是,您可以通过使用Twitter的javascript的api令牌来摆脱困境,从而不必使用自己的令牌.从理论上讲,这听起来很容易,但实际上并非如此.不过,这是hhb_curl的示例实现(来自https://github.com/divinity76/hhb_.inc.php/blob/master/hhb_.inc.php ),获取您的关注者列表,并使用自己的Twitters向每个人发送关注请求api密钥(自动提取):

now, the next step is to get all your current followers, and send a follow request. you say you don't want to use the api, but THERE IS LITERALLY NO WAY to avoid it (short of hacking the twitter databases, ofc), even tiwtter's javascript's "follow" button uses the api. good news is, you can get away by using twitter's javascript's api token, and thus not having to use your own token. this may sound easy in theory, but its really not. never the less, here's an example implementation with hhb_curl (from https://github.com/divinity76/hhb_.inc.php/blob/master/hhb_.inc.php ), getting a list of your followers, and sending a follow request to each one, using twitters own api key (extracted automatically):

<?php
declare(strict_types = 1);
require_once ('hhb_.inc.php');
const USERNAME = '???';
const PASSWORD = '???';

$hc = new hhb_curl ( 'https://twitter.com/login', true );
$hc->exec ();
// get csrf token
$csrf_token = [ ];
preg_match ( '/\s+ct0\s*=\s*(.*?)\;/', implode ( "\n", $hc->getResponseHeaders () ), $csrf_token );
if (count ( $csrf_token ) !== 2) {
    throw new Exception ( 'failed to extract the csrf token!' );
}
$csrf_token = $csrf_token [1];
// to log in...

$html = $hc->getStdOut ();
$domd = @DOMDocument::loadHTML ( $html );
$inputs = getDOMDocumentFormInputs ( $domd, true ) [0]; // << not sure why, but they have 6 seemingly duplicate login forms. the first 1 works fine.
$inputs = DOMInputsToArray ( $inputs );
$inputs ['session[username_or_email]'] = USERNAME;
$inputs ['session[password]'] = PASSWORD;
// hhb_var_dump ( $inputs ) & die ();
$html = $hc->setopt_array ( array (
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => http_build_query ( $inputs ),
        CURLOPT_URL => 'https://twitter.com/sessions' 
) )->exec ()->getResponseBody ();
$domd = @DOMDocument::loadHTML ( $html );
$xpath = new DOMXPath ( $domd );
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut () );
if (false !== stripos ( $hc->getinfo ( CURLINFO_EFFECTIVE_URL ), 'login/error' )) {
    throw new Exception ( 'failed to login!' );
}
echo "logged in!", PHP_EOL;
// now to get the api key
$js = $hc->exec ( 'https://abs.twimg.com/k/en/init.en.c5a67fc1f42cedcdbbcd.js' )->getResponseBody ();
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut () ) & die ();
// fragile regex: assumes that there's only 1x i="114 characters"; , and that the api key is exactly 114 characters.
preg_match ( '/i\s*\=\s*\"([^\"]{114})\"\s*\;/iu', $js, $matches );
// hhb_var_dump ( $matches ) & die ();
if (count ( $matches ) !== 2) {
    throw new RuntimeException ( 'failed to extract the api auth key!' );
}
$api_auth_key = $matches [1];

$myurl = 'https://twitter.com/' . ltrim ( $xpath->query ( '//a[contains(@class,\'DashboardProfileCard\')]' )->item ( 0 )->getAttribute ( "href" ), '/' );
echo 'myurl: ' . $myurl . PHP_EOL;
// $myurl = 'https://twitter.com/scaleway';
$myurl .= '/followers';
$html = $hc->exec ( $myurl )->getResponseBody ();
// hhb_var_dump ( $hc->getStdErr (), $hc->getStdOut () ) & die ();
$toFollow = array ();
$domd = @DOMDocument::loadHTML ( $html );
$xpath = new DOMXPath ( $domd );
foreach ( $xpath->query ( '//div[contains(@class,\'ProfileCard-content\')]' ) as $followerDiv ) {
    $name = $xpath->query ( './/*[@data-screen-name]', $followerDiv )->item ( 0 )->getAttribute ( "data-screen-name" );
    $user_id = $xpath->query ( './/*[@data-user-id]', $followerDiv )->item ( 0 )->getAttribute ( "data-user-id" );
    echo "following " . $name . ' (' . $user_id . ')' . PHP_EOL;
    try {
        $hc->setopt_array ( array (
                CURLOPT_CUSTOMREQUEST => 'OPTIONS',
                CURLOPT_URL => 'https://api.twitter.com/1.1/friendships/create.json',
                CURLOPT_HTTPHEADER => array (
                        'Access-Control-Request-Method: POST',
                        'Access-Control-Request-Headers: authorization,x-csrf-token,x-twitter-active-user,x-twitter-auth-type',
                        'DNT: 1',
                        'Origin: https://twitter.com' 
                ) 
        ) )->exec ();
    } catch ( Throwable $ex ) {
        // there is a bug where it sometimes respond http 200 OK, but with 0 bytes content. hhb_curl doesn't like this, as 0-bytes-responses should actually be http 201.
        // feel free to contact twitter with a bugreport.
    }
    // hhb_var_dump ( $hc->getStdErr () );
    $hc->setopt ( CURLOPT_CUSTOMREQUEST, NULL );
    $hc->setopt_array ( array (
            CURLOPT_POST => true,
            CURLOPT_URL => 'https://api.twitter.com/1.1/friendships/create.json',
            CURLOPT_POSTFIELDS => http_build_query ( array (
                    'challenges_passed' => 'false',
                    'handles_challenges' => '1',
                    'impression_id' => '',
                    'include_blocked_by' => 'true',
                    'include_blocking' => 'true',
                    'include_can_dm' => 'true',
                    'include_followed_by' => 'true',
                    'include_mute_edge' => 'true',
                    'skip_status' => 'true',
                    'user_id' => $user_id 
            ) ),
            CURLOPT_HTTPHEADER => array (
                    'Accept: application/json, text/javascript, */*; q=0.01',
                    'Accept-Language: en-US,en;q=0.5',
                    'Authorization: Bearer ' . $api_auth_key,
                    'x-twitter-auth-type: OAuth2Session',
                    'x-csrf-token: ' . $csrf_token,
                    'X-Twitter-Active-User: yes',
                    'DNT: 1',
                    'Origin: https://twitter.com',
                    'Referer: ' . $myurl 

            ) 

    ) )->exec ();

    // parse_str ( $hc->getopt ( CURLOPT_POSTFIELDS ), $fields );
    // hhb_var_dump ( $fields, $hc->getStdErr (), $hc->getStdOut () ) & die ();
}

// hhb_var_dump ( $myurl );
function DOMInputsToArray($inputs): array {
    $ret = [ ];
    foreach ( $inputs as $in ) {
        if ($in->hasAttribute ( "disabled" )) {
            continue;
        }
        $name = $in->getAttribute ( "name" );
        if (empty ( $name )) {
            continue;
        }
        $ret [$name] = $in->getAttribute ( "value" );
    }
    return $ret;
}
function getDOMDocumentFormInputs(\DOMDocument $domd, bool $getOnlyFirstMatches = false): array {
    // :DOMNodeList?
    $forms = $domd->getElementsByTagName ( 'form' );
    $parsedForms = array ();
    $isDescendantOf = function (\DOMNode $decendant, \DOMNode $ele): bool {
        $parent = $decendant;
        while ( NULL !== ($parent = $parent->parentNode) ) {
            if ($parent === $ele) {
                return true;
            }
        }
        return false;
    };
    // i can't use array_merge on DOMNodeLists :(
    $merged = function () use (&$domd): array {
        $ret = array ();
        foreach ( $domd->getElementsByTagName ( "input" ) as $input ) {
            $ret [] = $input;
        }
        foreach ( $domd->getElementsByTagName ( "textarea" ) as $textarea ) {
            $ret [] = $textarea;
        }
        return $ret;
    };
    $merged = $merged ();
    foreach ( $forms as $form ) {
        $inputs = function () use (&$domd, &$form, &$isDescendantOf, &$merged): array {
            $ret = array ();
            foreach ( $merged as $input ) {
                // hhb_var_dump ( $input->getAttribute ( "name" ), $input->getAttribute ( "id" ) );
                if ($input->hasAttribute ( "disabled" )) {
                    // ignore disabled elements?
                    continue;
                }
                $name = $input->getAttribute ( "name" );
                if ($name === '') {
                    // echo "inputs with no name are ignored when submitted by mainstream browsers (presumably because of specs)... follow suite?", PHP_EOL;
                    continue;
                }
                if (! $isDescendantOf ( $input, $form ) && $form->getAttribute ( "id" ) !== '' && $input->getAttribute ( "form" ) !== $form->getAttribute ( "id" )) {
                    // echo "this input does not belong to this form.", PHP_EOL;
                    continue;
                }
                if (! array_key_exists ( $name, $ret )) {
                    $ret [$name] = array (
                            $input 
                    );
                } else {
                    $ret [$name] [] = $input;
                }
            }
            return $ret;
        };
        $inputs = $inputs (); // sorry about that, Eclipse gets unstable on IIFE syntax.
        $hasName = true;
        $name = $form->getAttribute ( "id" );
        if ($name === '') {
            $name = $form->getAttribute ( "name" );
            if ($name === '') {
                $hasName = false;
            }
        }
        if (! $hasName) {
            $parsedForms [] = array (
                    $inputs 
            );
        } else {
            if (! array_key_exists ( $name, $parsedForms )) {
                $parsedForms [$name] = array (
                        $inputs 
                );
            } else {
                $parsedForms [$name] [] = $tmp;
            }
        }
    }
    unset ( $form, $tmp, $hasName, $name, $i, $input );
    if ($getOnlyFirstMatches) {
        foreach ( $parsedForms as $key => $val ) {
            $parsedForms [$key] = $val [0];
        }
        unset ( $key, $val );
        foreach ( $parsedForms as $key1 => $val1 ) {
            foreach ( $val1 as $key2 => $val2 ) {
                $parsedForms [$key1] [$key2] = $val2 [0];
            }
        }
    }
    return $parsedForms;
}

输出:已登录!myurl:https://twitter.com/HansHenrik_关注tianwm(53056654)在burburcinar之后(2335591322)跟随DnR_iData(260134525)正在关注7wData(1713417312)在Deepudeepana之后(783199483404226560)正在关注remco_verhoef(201001391)关注PaulVlasin(1079477118)^ C(我不想在所有人的关注下在^ C手动将其取消,但这足以证明它可以正常工作)-并且不要忘记在第4行和第6行中替换用户名/密码.第5行-请注意,由于我没有任何关注者,因此我使用了 https://twitter.com/scaleway/followers 作为具有大量关注者的测试页,您可以在注释掉的第50行看到我在哪里伪造了该网址.-而且,它可能只会向所有关注者发送请求,您可以在关注者页面,如果您有很多关注者,则不是完整列表(这会使您的浏览器崩溃),因此,如果您有大量关注者页面,则必须了解如何获取完整的关注者列表关注者-

output: logged in! myurl: https://twitter.com/HansHenrik_ following tianwm (53056654) following theburakcinar (2335591322) following DnR_iData (260134525) following 7wData (1713417312) following deepudeepana (783199483404226560) following remco_verhoef (201001391) following PaulVlasin (1079477118) ^C (i manually canceled it at ^C, as i dont want to follow everyone, but it was enough to prove that it worked) - and don't forget to replace username/password on line 4 & line 5 - and note that since i don't have any followers, i used https://twitter.com/scaleway/followers as a test page with lots of followers, you can see where i faked the url at the commented-out line 50. - also, it will probably just send a request to all the followers you can see on the followers page, which, if you have a lot of followers, is not the full list (this would crash your browser etc), so you'll have to find out how to get the full list of followers if you have a big amount of followers -

这篇关于使用cURL,PHP和Twitter而不使用API​​的逻辑的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆