如何抓取 Instagram数据
社交媒体爬虫提供了一种收集有价值数据的绝佳方式,无论是用于研究还是商业目的。而Instagram可能是当今最具利润的平台。然而,由于技术和法律挑战,对其进行爬取也是棘手的。
在本指南中,您将了解在不惹麻烦的情况下可以爬取哪些Instagram数据,以及应该选择哪些工具来避免IP地址封禁。此外,您将找到两个使用Python构建基本Instagram爬虫的逐步指南-一个使用Requests,另一个使用Selenium。

什么是Instagram爬取?
Instagram爬取是自动从社交媒体平台收集公开可用数据的过程。根据您的编程知识,可以使用预先制作的爬虫工具或定制的网页爬虫进行爬取。
社交媒体营销人员知道,数据收集可以为您带来全新的机会。通过收集诸如标签或帖子等信息,您可以进行市场和情感分析,监控在线品牌,或为您的业务寻找影响者。
如何合法地进行Instagram爬取
网络爬取仍然是一个法律上的灰色地带,尤其是涉及社交媒体时更是如此。我们不是律师,但通常认为爬取网站是可以的(尽管网站管理员可能不希望您这样做),前提是:a)数据是公开可用的,b)不涉及受版权保护的内容,c)或个人信息。
Instagram也不例外。只要您小心谨慎,爬取该平台是合法的。但是,如果有一件您绝对不应该做的事情,那就是收集登录后的数据。如果Meta发现了您的活动,这是一定会被起诉的。
最后,根据使用情况,法规会施加不同的标准。例如,如果您收集信息进行研究,相对而言,会更有自由度,而如果是商业目的,就会受到更多限制。如果您对自己的情况不确定,最好咨询一位律师。
那么,在不登录的情况下,您可以爬取哪些数据呢?
有三个主要类别的公开可用数据:
- 标签:帖子URL,媒体URL,帖子作者ID。
- 配置文件:最新帖子,外部URL,点赞数,图片,评论,每条帖子的点赞数和粉丝数。
- 帖子:最新帖子,日期,URL,评论,点赞,作者ID。
但要记住,Instagram经常更改规则,因此在实际进行爬取之前,最好查看您可以爬取的内容。
选择您的Instagram爬取工具
通常有三种类型的工具可用于爬取Instagram:1)自定义网页爬虫,2)网页爬取API,或3)现成的网页爬虫。
如果您具有编程知识,可以尝试使用网页爬取框架(如Selenium或Playwright)构建自己的网页爬虫。这样可以处理复杂的自动化,并且由于您负责您的爬取机器人,因此可以适应Instagram提供的所有结构性更改。
Instagram不再有官方API(即使有也没有什么用)。但是有许多可靠的供应商提供网页爬取API。例如,Apify提供了各种API来收集各种Instagram数据点,例如Instagram配置文件爬取器和帖子爬取器。或者,您可以使用基于大型代理池的通用网页爬取器,如Smartproxy的Web Scraping API或Zyte的Smart Proxy Manager。
如果您没有任何编程技能,可以购买现成的爬取器,例如Parsehub,Octoparse或Bright Data的Data Collector。这些工具允许您通过直观地单击元素或使用便捷的模板提取数据。
如何爬取Instagram数据:使用Python的逐步指南
那么假设您想自己尝试爬取Instagram。该如何进行?
我们将尝试构建两个简单的网页爬虫。一个使用Requests,这是一个流行的Python网页爬取库。第二种方法使用Selenium启动一个无界面的Chrome实例。它们的区别如下:
Selenium模拟浏览器 — 它打开并访问网页。使用Selenium进行爬取将带来更多成功的请求。而Requests库则仅将HTTP请求发送给浏览器。此方法的成功率较低,但您可以更快地爬取Instagram。
开始爬取Instagram还需要其他必要的工具
如果你想安全地开始搜索 Instagram,还必须考虑隐藏自己的 IP 地址,因为该平台限制用户在未登录的情况下访问的信息量。
最好的方法是使用轮换代理服务器。根据您的代理提供商,它会在每五分钟、10分钟或每次连接请求时给您一个不同的IP地址。如果您不知道如何获取代理,可以查看我们列出的出色的Instagram代理提供商的列表。
预期管理
爬取社交媒体平台是一个困难且复杂的过程。您必须保持耐心,以增加成功爬取Instagram数据的机会。
您的一些请求不可避免地会失败 — 20%、30%甚至更多,直到您掌握了这个技巧。失败的请求将导致代理错误,停止爬虫。您可以添加重试功能以重试,但是为了这样做,您需要在每次失败的请求后更改您的IP地址。
如何使用 Selenium 抓取 Instagram 的公共配置文件
现在,大多数在线媒体,包括Instagram,要求用户提供个人信息以访问网站或特定内容。他们将数据放在像手机号码、电子邮件地址或一些问题之类的潜在收集表单后面。不久的将来,您可能会发现更少的数据点可以进行爬取,因为Instagram将会封锁更多内容。
这是使用代理的真实示例。我们将使用__a=1参数来爬取Instagram用户名,该参数可以将任何页面转换为JSON格式。在本指南中,该参数将返回来自个人资料页面的内容。
步骤1.首先安装Selenium,Chromedriver和Selenium-Stealth。
1)从Selenium模块中导入webdriver。
from selenium import webdriver
2)然后使用Selenium的By选择器模块导入web driver以简化选择。
from selenium.webdriver.common.by import By
3)将结果打印出来以格式化控制台输出。
from pprint import pprint
4)由于我们将使用JSON模块,您还需要导入它。
import json
5)然后导入Selenium-Stealth以获得更真实的浏览器效果。
from selenium_stealth import stealth
步骤2.设置您希望爬取的Instagram配置文件的用户名。
usernames = ["jlo", "shakira", "beyonce", "katyperry"]
创建代理变量。它们将帮助您获得更高的成功率。
proxy = "server:port"
您可以创建一个新的字典变量来存储爬取的结果。
output = {}
步骤3.然后编写代码的开头,调用main()函数,并在完成后添加另一行以打印出爬取结果。
if __name__ == '__main__':
main()
pprint(output)
编写代码来迭代您要爬取的用户名。main()函数将遍历Instagram用户名列表,并将其发送到我们稍后将编写的另一个scrape()函数中。
def main():
for username in usernames:
scrape(username)
步骤4.接下来,您需要按照以下步骤定义该函数:
1)创建一个新函数。它将允许您在每次爬取之前对浏览器设置进行更改,比如更改用户代理或旋转代理。
def prepare_browser():
2)初始化Chrome选项。
chrome_options = webdriver.ChromeOptions()
3)将代理添加到浏览器选项。
chrome_options.add_argument(f'--proxy-server={proxy}')
4)现在让我们指定Selenium-Stealth需要的设置。
chrome_options.add_argument("start-maximized")
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
5)使用之前设置的选项创建Chrome浏览器。
driver = webdriver.Chrome(options= chrome_options)
6)应用更多Selenium-Stealth设置。为了增加匿名性,您可以旋转您的数字指纹或用户代理。
stealth(driver,
user_agent= 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.53 Safari/537.36',
languages= ["en-US", "en"],
vendor= "Google Inc.",
platform= "Win32",
webgl_vendor= "Intel Inc.",
renderer= "Intel Iris OpenGL Engine",
fix_hairline= False,
run_on_insecure_origins= False,
)
7)返回具有到目前为止设置的所有选项和设置的Chrome驱动器。
return driver
步骤5.现在让我们继续进行爬取。
1)创建一个新函数。scrape()函数只需要一个参数 — 从main()函数中传递的用户名。
def scrape(username):
2)构建URL。在末尾添加?__a=1&__d=dis允许您直接从Instagram后端获取响应,而无需解析HTML内容。
url = f'https://instagram.com/{username}/?__a=1&__d=dis'
3)然后调用prepare_browser()函数并将驱动程序分配给一个变量。
chrome = prepare_browser()
4)打开浏览器并发出请求。
chrome.get(url)
5)要判断请求是否失败,您需要检查是否被重定向到登录页面。您可以通过查看URL中是否存在登录字符串来进行检查,如果存在,则请求未成功。在这里可以添加额外的重试功能,稍后尝试爬取用户名。
print (f"Attempting: {chrome.current_url}")
if "login" in chrome.current_url:
print ("Failed/ redir to login")
chrome.quit()
6)否则,请求成功。这意味着我们可以从响应中提取正文文本,并将其解析为JSON。结果随后可以与您爬取的Instagram用户名一起传递给parse_data()函数。
else:
print ("Success")
resp_body = chrome.find_element(By.TAG_NAME, "body").text
data_json = json.loads(resp_body)
user_data = data_json['graphql']['user']
parse_data(username, user_data)
chrome.quit()
步骤6.让我们继续解析数据。
创建一个名为parse_data()的函数,以从JSON响应中获取您想要的数据。
def parse_data(username, user_data):
例如,您可以从公开可用的帖子中获取一些帖子标题。
captions = []
if len(user_data['edge_owner_to_timeline_media']['edges']) > 0:
for node in user_data['edge_owner_to_timeline_media']['edges']:
if len(node['node']['edge_media_to_caption']['edges']) > 0:
if node['node']['edge_media_to_caption']['edges'][0]['node']['text']:
captions.append(
node['node']['edge_media_to_caption']['edges'][0]['node']['text']
)
除了帖子标题,您还可以获取用户的全名,所属类别和粉丝数。所有这些信息最终都可以写入输出字典。
output[username] = {
'name': user_data['full_name'],
'category': user_data['category_name'],
'followers': user_data['edge_followed_by']['count'],
'posts': captions,
}
这是脚本的输出结果:
from selenium import webdriver
from selenium.webdriver.common.by import By
from pprint import pprint
import json
from selenium_stealth import stealth
usernames = ["jlo", "shakira", "beyonce", "katyperry"]
output = {}
def prepare_browser():
chrome_options = webdriver.ChromeOptions()
proxy = "server:port"
chrome_options.add_argument(f'--proxy-server={proxy}')
chrome_options.add_argument("start-maximized")
chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])
chrome_options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options= chrome_options)
stealth(driver,
user_agent= 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.53 Safari/537.36',
languages= ["en-US", "en"],
vendor= "Google Inc.",
platform= "Win32",
webgl_vendor= "Intel Inc.",
renderer= "Intel Iris OpenGL Engine",
fix_hairline= False,
run_on_insecure_origins= False,
)
return driver
def parse_data(username, user_data):
captions = []
if len(user_data['edge_owner_to_timeline_media']['edges']) > 0:
for node in user_data['edge_owner_to_timeline_media']['edges']:
if len(node['node']['edge_media_to_caption']['edges']) > 0:
if node['node']['edge_media_to_caption']['edges'][0]['node']['text']:
captions.append(
node['node']['edge_media_to_caption']['edges'][0]['node']['text']
)
output[username] = {
'name': user_data['full_name'],
'category': user_data['category_name'],
'followers': user_data['edge_followed_by']['count'],
'posts': captions,
}
def scrape(username):
url = f'https://instagram.com/{username}/?__a=1&__d=dis'
chrome = prepare_browser()
chrome.get(url)
print (f"Attempting: {chrome.current_url}")
if "login" in chrome.current_url:
print ("Failed/ redir to login")
chrome.quit()
else:
print ("Success")
resp_body = chrome.find_element(By.TAG_NAME, "body").text
data_json = json.loads(resp_body)
user_data = data_json['graphql']['user']
parse_data(username, user_data)
chrome.quit()
def main():
for username in usernames:
scrape(username)
if __name__ == '__main__':
main()
pprint(output)
如何使用Requests库爬取Instagram的公开配置文件
这是另一个逐步示例,使用基于Python的Requests库。这种方法速度更快,更轻量级,因为您无需模拟Web浏览器。然而,它也会更多地失败。但即使成功率较低,通过简单地使用新的代理重试,您仍然可以爬取相当数量的数据。
步骤1.首先导入Requests,JSON和Random。
import requests, json, random
然后我们将打印出结果以格式化控制台输出。
from pprint import pprint
步骤2.现在让我们设置一个包含我们要爬取的所有Instagram用户的用户名列表。
usernames = ["jlo", "shakira", "beyonce", "katyperry"]
之后,设置您的代理。
proxy = "http://username:password@server:port"
您可以创建一个新的字典变量来存储爬取的结果。
output = {}
步骤3.然后编写代码的开头,调用main()函数。
if __name__ == '__main__':
main()
pprint(output)
现在准备头信息和掩码,通过爬虫发送请求。这些标题还将旋转几个用户代理。
def get_headers(username):
headers = {
"authority": "www.instagram.com",
"method": "GET",
"path": "/{0}/".format(username),
"scheme": "https",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding" : "gzip, deflate, br",
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
"upgrade-insecure-requests": "1",
"Connection": "close",
"user-agent" : random.choice([
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
])
}
return headers
步骤4.编写代码来迭代您要爬取的用户名。main()函数将遍历Instagram用户名列表。
def main():
for username in usernames:
url = f"https://instagram.com/{username}/?__a=1&__d=dis"
步骤5.现在,我们将编写将发送请求并将头信息和代理应用于请求的行。
response = requests.get(url, headers=get_headers(username),
proxies = {'http': proxy, 'https': proxy})
要判断请求是否失败,您需要检查是否被重定向到登录页面。您可以通过检查响应是否为JSON来进行检查。此外,此行将允许您解析响应文本。
if response.status_code == 200:
try:
resp_json = json.loads(response.text)
如果未获取JSON结果,则表示您被重定向到登录页面;脚本将转到下一个用户名。
except:
print ("Failed. Response not JSON")
continue
在这里可以添加额外的重试功能,稍后尝试爬取用户名。
else:
如果您获得了JSON结果,那么您可以清理(解析)数据。
user_data = resp_json['graphql']['user']
parse_data(username, user_data)
在整个过程中可能会出现一些错误。让我们尝试捕获并处理它们。如果失败,使用重试逻辑。
elif response.status_code == 301 or response.status_code ==
302:
print ("Failed. Redirected to login")
else:
print("Request failed. Status: " + str(response.status_code))
步骤6.创建parse_data()函数以从JSON响应中获取您想要的数据。
def parse_data(username, user_data):
例如,您可以从公开可用的帖子中获取一些帖子标题,并将它们分配给一个变量列表。
captions = []
if len(user_data['edge_owner_to_timeline_media']['edges']) > 0:
for node in user_data['edge_owner_to_timeline_media']['edges']:
if node['node']['edge_media_to_caption']['edges'][0]['node']['text']:
captions.append(
node['node']['edge_media_to_caption']['edges'][0]['node']['text']
)
output[username] = {
'name': user_data['full_name'],
'category': user_data['category_name'],
'followers': user_data['edge_followed_by']['count'],
'posts': captions,
}
这是脚本的输出结果:
import requests, json, random
from pprint import pprint
usernames = ["jlo", "shakira", "beyonce", "katyperry"]
proxy = "http://username:password@proxy:port"
output = {}
def get_headers(username):
headers = {
"authority": "www.instagram.com",
"method": "GET",
"path": "/{0}/".format(username),
"scheme": "https",
"accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
"accept-encoding" : "gzip, deflate, br",
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
"upgrade-insecure-requests": "1",
"Connection": "close",
"user-agent" : random.choice([
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36"
])
}
return headers
def parse_data(username, user_data):
captions = []
if len(user_data['edge_owner_to_timeline_media']['edges']) > 0:
for node in user_data['edge_owner_to_timeline_media']['edges']:
if len(node['node']['edge_media_to_caption']['edges']) > 0:
if node['node']['edge_media_to_caption']['edges'][0]['node']['text']:
captions.append(
node['node']['edge_media_to_caption']['edges'][0]['node']['text']
)
output[username] = {
'name': user_data['full_name'],
'category': user_data['category_name'],
'followers': user_data['edge_followed_by']['count'],
'posts': captions,
}
def main():
for username in usernames:
url = f"https://instagram.com/{username}/?__a=1&__d=dis"
response = requests.get(url, headers=get_headers(username), proxies = {'http': proxy, 'https': proxy})
if response.status_code == 200:
try:
resp_json = json.loads(response.text)
except:
print ("Failed. Response not JSON")
continue
else:
user_data = resp_json['graphql']['user']
parse_data(username, user_data)
elif response.status_code == 301 or response.status_code == 302:
print ("Failed. Redirected to login")
else:
print("Request failed. Status: " + str(response.status_code))
if __name__ == '__main__':
main()
pprint(output)