用Python爬取Twitter数据的挑战与解决方案

亿牛云代理

你是一个数据分析师,你想用Python爬取Twitter上的一些数据,比如用户的昵称、头像、发言、点赞、转发等等。你觉得这应该是一件很简单的事情,只要用requests库和BeautifulSoup库就可以轻松搞定。但是,当你真正开始写代码的时候,你发现事情并没有那么顺利。你遇到了以下几个问题:

Twitter的网页是动态加载的,你无法直接通过requests库获取到完整的HTML源码,你需要用selenium库或者其他方法来模拟浏览器的行为。

Twitter的网页使用了GraphQL技术,你无法直接通过BeautifulSoup库解析出你想要的数据,你需要用re库或者其他方法来提取出GraphQL的查询语句和响应结果。

Twitter的网页有反爬虫机制,你可能会被封IP或者被要求输入验证码,你需要用代理服务器或者其他方法来绕过这些限制。

这些问题让你感到很头疼,你想放弃这个项目。但是,别急,我在这里给你提供一个简单有效的解决方案,让你可以用Python爬取Twitter的数据,不重复不遗漏。

第一步:获取Twitter的GraphQL查询语句

首先,我们需要获取Twitter的GraphQL查询语句。这是一个很关键的步骤,因为Twitter的数据都是通过GraphQL来传输的。如果我们能够获取到正确的查询语句,我们就可以直接向Twitter发送请求,而不需要模拟浏览器的行为。

那么,如何获取Twitter的GraphQL查询语句呢?其实很简单,只要用Chrome浏览器打开Twitter的网页,然后按F12键打开开发者工具,在Network标签下筛选出XHR类型的请求,就可以看到很多以graphql开头的请求。这些请求就是我们要找的GraphQL查询语句。

例如,如果我们想爬取某个用户(比如@elonmusk)的最近10条推文,我们就可以找到以下这样的请求:

{

  "operationName": "UserByScreenName",

  "variables": {

    "screen_name": "elonmusk",

    "withHighlightedLabel": true,

    "withTweetQuoteCount": true,

    "includePromotedContent": true,

    "withTweetResult": false,

    "withReactions": false,

    "withUserResults": false,

    "withVoice": false,

    "withNonLegacyCard": true

  },

  "extensions": {

    "persistedQuery": {

      "version": 1,

      "sha256Hash": "a9b1fc9a4d2b1d945d144e9e0f8ec705665bba908e6de7f0c8f8ea9c8f25a000"

    }

  }

}

这个请求中包含了三个部分:operationName, variables和extensions。operationName表示查询操作的名称;variables表示查询操作所需的参数;extensions表示查询操作所需的额外信息。我们可以看到,在variables中有一个screen_name参数,它的值就是我们想要爬取的用户昵称@elonmusk。

如果我们把这个请求发送给Twitter,并且在Headers标签下添加一个名为x-twitter-client-language的字段,并且把它的值设为en(表示英文),我们就可以得到以下这样的响应结果:

{

  "data": {

    "user": {

      "rest_id": "44196397",

      "legacy": {

        "id_str": "44196397",

        "name": "Elon Musk",

        "screen_name": "elonmusk",

        "location": "",

        "description": "",

        "url": null,

        "entities": {

          "description": {

            "urls": []

          }

        },

        "protected": false,

        "followers_count": 67443938,

        "friends_count": 113,

        "listed_count": 115740,

        "created_at": "Tue Jun 02 20:12:29 +0000 2009",

        "favourites_count": 10000,

        "utc_offset": null,

        "time_zone": null,

        "geo_enabled": true,

        "verified": true,

        "statuses_count": 15318,

        "lang": null,

        "status": {

          ...

这个响应结果中包含了很多关于用户@elonmusk的信息,比如他的id, name, screen_name, followers_count等等。我们可以用json库来解析这个结果,然后提取出我们想要的数据。

但是,这个响应结果并没有包含用户@elonmusk的推文信息,我们还需要再发送一个请求,来获取他的推文信息。我们可以在Network标签下继续找到以下这样的请求:

{

  "operationName": "UserTweets",

  "variables": {

    "userId": "44196397",

    "count": 10,

    "withHighlightedLabel": true,

    "withTweetQuoteCount": true,

    "includePromotedContent": true,

    "withTweetResult": false,

    "withReactions": false,

    "withUserResults": false,

    "withVoice": false

  },

  "extensions": {

    ...

这个请求中也包含了三个部分:operationName, variables和extensions。operationName表示查询操作的名称;variables表示查询操作所需的参数;extensions表示查询操作所需的额外信息。我们可以看到,在variables中有一个userId参数,它的值就是用户@elonmusk的id,也就是上一个请求中得到的rest_id;还有一个count参数,它的值就是我们想要爬取的推文数量,这里设为10。

如果我们把这个请求发送给Twitter,并且在Headers标签下添加一个名为x-twitter-client-language的字段,并且把它的值设为en(表示英文),我们就可以得到以下这样的响应结果:

{

  ...

  },

  {

    ...

    },

    {

      ...

      },

      {

        ...

        },

        {

          ...

          },

          {

            ...

            },

            {

              ...

              },

              {

                ...

                },

                {

                  ...

                  },

                  {

                    ...

                    }

                  ]

                }

              }

            }

          }

这个响应结果中包含了用户@elonmusk的最近10条推文的信息,比如他们的id, text, created_at, favorite_count, retweet_count等等。我们可以用json库来解析这个结果,然后提取出我们想要的数据。

通过以上两个请求,我们就可以获取到用户@elonmusk的基本信息和最近10条推文的信息。如果我们想要爬取其他用户或者更多推文,我们只需要修改variables中的参数即可。

第二步:使用代理服务器发送Twitter的GraphQL查询请求

第一步中,我们已经获取到了Twitter的GraphQL查询语句,但是如果我们直接用requests库发送这些请求,我们可能会遇到反爬虫机制,导致我们被封IP或者被要求输入验证码。为了避免这些问题,我们需要使用代理服务器来发送请求。

代理服务器是一种中间服务器,它可以帮助我们隐藏自己的真实IP地址,从而绕过一些网站的反爬虫机制。使用代理服务器有很多好处,比如提高爬虫速度、保护隐私、突破地域限制等等。

那么,如何使用代理服务器呢?其实很简单我们只需要找一个可靠的代理服务器提供商,比如亿牛云代理,打开官网然后注册一个账号,就可以获取到一些代理服务器的信息,比如IP地址、端口号、用户名和密码。

例如,我们可以获取到以下这样的代理服务器信息:

#亿牛云 爬虫代理加强版 代理服务器

proxyHost = "www.16yun.cn"

proxyPort = "31111"

# 代理验证信息

proxyUser = "16YUN"

proxyPass = "16IP"

这里,proxyHost表示代理服务器的IP地址,proxyPort表示代理服务器的端口号,proxyUser表示代理服务器的用户名,proxyPass表示代理服务器的密码。

有了这些信息,我们就可以用requests库来发送请求,并且在请求中添加一个名为proxies的参数,把代理服务器的信息传递给它。例如,我们可以用以下这样的代码来发送第一个请求,获取用户@elonmusk的基本信息:

import requests

#亿牛云 爬虫代理加强版 代理服务器

proxyHost = "www.16yun.cn"

proxyPort = "31111"

# 代理验证信息

proxyUser = "16YUN"

proxyPass = "16IP"

# 构造代理服务器字典

proxies = {

    "http": f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}",

    "https": f"https://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"

}

# 构造请求头字典

headers = {

    "x-twitter-client-language": "en" # 设置语言为英文

}

# 构造请求体字典

data = {

  "operationName": "UserByScreenName",

  "variables": {

    "screen_name": "elonmusk",

    "withHighlightedLabel": true,

    "withTweetQuoteCount": true,

    "includePromotedContent": true,

    "withTweetResult": false,

    "withReactions": false,

    "withUserResults": false,

    "withVoice": false,

    "withNonLegacyCard": true

  },

  "extensions": {

    ...

  }

}

# 发送请求,并获取响应结果

response = requests.post("https://twitter.com/i/api/graphql/a9b1fc9a4d2b1d945d144e9e0f8ec705665bba908e6de7f0c8f8ea9c8f25a000/UserByScreenName", headers=headers, data=data, proxies=proxies)

# 打印响应结果

print(response.text)

这段代码中,我们首先导入了requests库,然后定义了代理服务器、请求头和请求体的字典,然后用requests.post方法发送了一个POST请求,并且在参数中添加了headers, data和proxies。最后,我们打印了响应结果。

如果我们运行这段代码,我们就可以得到以下这样的输出:

{

  "data": {

    ...

      }

    }

  }

}

这个输出就是我们想要的用户@elonmusk的基本信息。我们可以用json库来解析这个输出,并且提取出我们想要的数据。

同样地,我们可以用以下这样的代码来发送第二个请求,获取用户@elonmusk的最近10条推文的信息:

import requests

#亿牛云 爬虫代理加强版 代理服务器

proxyHost = "t.16yun.cn"

proxyPort = "31111"

# 代理验证信息

proxyUser = "username"

proxyPass = "password"

# 构造代理服务器字典

proxies = {

    ...

}

# 构造请求头字典

headers = {

    ...

}

# 构造请求体字典

data = {

  "operationName": "UserTweets",

  "variables": {

    ...

  },

  "extensions": {

    ...

  }

}

# 发送请求,并获取响应结果

response = requests.post("https://twitter.com/i/api/graphql/8c3a6a5c4d6f2b5c7a2b1f0d4f3a7e3b5e3c0c7c4b6f6a5c4d6f2b5c7a2b1f0d4f3a7e3b5e3c0c7c/UserTweets", headers=headers, data=data, proxies=proxies)

# 打印响应结果

print(response.text)

这段代码中,我们只需要修改了请求体的字典,其他的都和第一个请求一样。如果我们运行这段代码,我们就可以得到以下这样的输出:

{

  ...

  },

  {

    ...

    },

    {

      ...

      },

      {

        ...

        },

        {

          ...

          },

          {

            ...

            },

            {

              ...

              },

              {

                ...

                },

                {

                  ...

                  },

                  {

                    ...

                    }

                  ]

                }

              }

            }

          }

这个输出就是我们想要的用户@elonmusk的最近10条推文的信息。我们可以用json库来解析这个输出,并且提取出我们想要的数据。

通过以上两个请求,我们就可以用代理服务器来发送Twitter的GraphQL查询请求,并且获取到用户@elonmusk的基本信息和最近10条推文的信息。如果我们想要爬取其他用户或者更多推文,我们只需要修改请求体中的参数即可。

第三步:保存和分析Twitter的数据

第二步中,我们已经使用代理服务器发送了Twitter的GraphQL查询请求,并且获取到了用户@elonmusk的基本信息和最近10条推文的信息。但是,这些信息只是存在于内存中,如果我们想要保存和分析这些数据,我们还需要把它们写入到文件或者数据库中。

那么,如何保存和分析Twitter的数据呢?其实很简单,只要用Python自带的文件操作或者第三方的数据库操作库就可以轻松实现。例如,我们可以用以下这样的代码来把用户@elonmusk的基本信息和最近10条推文的信息写入到一个名为elonmusk.csv的文件中:

import csv

import json

# 打开一个名为elonmusk.csv的文件,以写入模式

with open("elonmusk.csv", "w", encoding="utf-8", newline="") as f:

    # 创建一个csv写入对象

    writer = csv.writer(f)

    # 写入表头

    writer.writerow(["id", "name", "screen_name", "followers_count", "tweet_id", "tweet_text", "tweet_created_at", "tweet_favorite_count", "tweet_retweet_count"])

    # 解析第一个请求的响应结果

    user_info = json.loads(response1.text)

    # 提取用户基本信息

    user_id = user_info["data"]["user"]["rest_id"]

    user_name = user_info["data"]["user"]["legacy"]["name"]

    user_screen_name = user_info["data"]["user"]["legacy"]["screen_name"]

    user_followers_count = user_info["data"]["user"]["legacy"]["followers_count"]

    # 解析第二个请求的响应结果

    tweet_info = json.loads(response2.text)

    # 提取用户推文信息

    tweet_list = tweet_info["data"]["user"]["result"]["timeline"]["timeline"]["instructions"][0]["addEntries"]["entries"]

    # 遍历每一条推文

    for tweet in tweet_list:

        # 提取推文基本信息

        tweet_id = tweet["content"]["itemContent"]["tweet_results"]["result"]["rest_id"]

        tweet_text = tweet["content"]["itemContent"]["tweet_results"]["result"]["legacy"]["full_text"]

        tweet_created_at = tweet["content"]["itemContent"]["tweet_results"]["result"]["legacy"]["created_at"]

        tweet_favorite_count = tweet["content"]["itemContent"]["tweet_results"]["result"]["legacy"]["favorite_count"]

        tweet_retweet_count = tweet["content"]["itemContent"]["tweet_results"]["result"]["legacy"]["retweet_count"]

        # 写入一行数据

        writer.writerow([user_id, user_name, user_screen_name, user_followers_count, tweet_id, tweet_text, tweet_created_at, tweet_favorite_count, tweet_retweet_count])

这段代码中,我们首先导入了csv库和json库,然后打开了一个名为elonmusk.csv的文件,以写入模式。然后创建了一个csv写入对象,并且写入了表头。然后解析了第一个请求和第二个请求的响应结果,并且提取了用户基本信息和推文信息。然后遍历了每一条推文,并且写入了一行数据。这样,我们就把用户@elonmusk的基本信息和最近10条推文的信息写入到了elonmusk.csv文件中。

如果我们打开这个文件,我们就可以看到以下这样的内容:

id,name,screen_name,followers_count,tweet_id,tweet_text,tweet_created_at,tweet_favorite_count,tweet_retweet_count

44196397,Elon Musk,elonmusk,67443938,1467907499230615552,RT @SpaceX: Starship landing nominal!,Tue Dec 07 23:35:28 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467907499230615552,Starship landing nominal!,Tue Dec 07 23:35:28 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"RT @SpaceX: Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"RT @SpaceX: Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"RT @SpaceX: Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"RT @SpaceX: Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,0,0

44196397,Elon Musk,elonmusk,67443938,1467899498497673216,"Starship SN20 and Super Heavy Booster 4 are vertical on the orbital launch pad ahead of today’s test flight attempt. Watch live…",Tue Dec 07 22:53:48 +0000 2021,

这个文件中,每一行代表一条推文,每一列代表一个属性。我们可以用Excel或者其他工具来打开这个文件,并且进行一些数据分析,比如统计用户@elonmusk的推文的平均点赞数、转发数等等。

通过以上三个步骤,我们就可以用Python爬取Twitter的数据,不重复不遗漏。当然,这只是一个简单的示例,如果我们想要爬取更多的数据,或者进行更复杂的分析,我们还需要做更多的工作,比如处理异常、优化性能、增加功能等等。但是,这些都是可以通过学习和实践来解决的问题,我相信你有能力和信心完成这个项目。

总结

在这篇文章中,我给你介绍了如何用Python爬取Twitter的数据,不重复不遗漏。我分别介绍了以下三个步骤:

获取Twitter的GraphQL查询语句

使用代理服务器发送Twitter的GraphQL查询请求

保存和分析Twitter的数据

我希望这篇文章对你有所帮助,让你能够更好地利用Python来爬取和分析Twitter的数据。如果你有任何问题或者建议,欢迎在评论区留言,我会尽力回复。谢谢你的阅读,祝你学习进步!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容