怎么爬墙(怎么爬梯子)
直接介绍一下具体的步骤以及注意点:
instagram 爬虫注意点
instagram 的首页数据是 服务端渲染的,所以首页出现的 22 或 12 条数据是以 html 中的一个 json 结构存在的, 之后的帖子加载才是走 ajax 请求的在 2019/06 之前,ins 是有反爬机制的,请求时需要在请求头加了 'X-Instagram-GIS' 字段。其算法是:
1、将 rhx_gis 和 queryVariables 进行组合
rhx_gis 可以在首页处的 sharedData 这个 json 结构中获得
2、然后进行 md5 哈希
e.g.
queryVariables = '{"id":"' + user_id + '","first":12,"after":"' +cursor+ '"}' print headers['X-Instagram-GIS'] = hashStr
但是在在 2019/06 之后, instagram 已经取消了 X-Instagram-GIS 的校验,所以无需再生成 X-Instagram-GIS,上一点内容可以当做历史来了解了
初始访问 ins 首页的时候会设置一些 cookie,设置的内容 如下:
set-cookie: rur=PRN; Domain=.instagram.com; HttpOnly; Path=/; Secure set-cookie: ds_user_id=22859524403; Domain=.instagram.com; expires=Mon, 15-Jul-2019 09:22:48 GMT; Max-Age=7776000; Path=/; Secure set-cookie: urlgen="{\\"45.63.123.251\\": 20473}:1hGKIi:7bh3mEau4gMVhrzWRTvtjs9hJ2Q"; Domain=.instagram.com; HttpOnly; Path=/; Secure set-cookie: csrftoken=Or4nQ1T3xidf6CYyTE7vueF46B73JmAd; Domain=.instagram.com; expires=Tue, 14-Apr-2020 09:22:48 GMT; Max-Age=31449600; Path=/; Secure
关于 query_hash,一般这个哈希值不用怎么管,可以直接写死
特别注意:在每次请求时务必带上自定义的 header,且 header 里面要有 user-agent,这样子才能使用 rhx_gis 来进行签名访问并且获取到数据。切记!是每次访问!例如:
headers = { 'user-agent': 'Mozilla/5.0 AppleWebKit/537.36 Chrome/68.0.3440.106 Safari/537.36'
大部分 api 的访问需要在请求头的 cookie 中携带 session-id 才能得到数据,一个正常的请求头 如下:
:authority: www.instagram.com :method: GET :path: /graphql/query/?query_hash=ae21d996d1918b725a934c0ed7f59a74&variables=%7B%22fetch_media_count%22%3A0%2C%22fetch_suggested_count%22%3A30%2C%22ignore_cache%22%3Atrue%2C%22filter_followed_friends%22%3Atrue%2C%22seen_ids%22%3A%5B%5D%2C%22include_reel%22%3Atrue%7D :scheme: https accept: */* accept-encoding: gzip, deflate, br accept-language: zh-CN,zh;q=0.9,en;q=0.8,la;q=0.7 cache-control: no-cache cookie: mid=XI-joQAEAAHpP4H2WkiI0kcY3sxg; csrftoken=Or4nQ1T3xidf6CYyTE7vueF46B73JmAd; ds_user_id=22859524403; sessionid=22859524403%3Al965tcIRCjXmVp%3A25; rur=PRN; urlgen="{\\"45.63.123.251\\": 20473}:1hGKIj:JvyKtYz_nHgBsLZnKrbSq0FEfeg" pragma: no-cache referer: https://www.instagram.com/ user-agent: Mozilla/5.0 AppleWebKit/537.36 Chrome/73.0.3683.103 Safari/537.36 x-ig-app-id: 936619743392459 x-instagram-gis: 8f382d24b07524ad90b4f5ed5d6fccdb x-requested-with: XMLHttpRequest
注意 user-agent、x-ig-app-id 、x-instagram-gis,以及 cookie 中的 session-id 配置
api 的分页 ,如用户帖子列表ins 中一个带分页的 ajax 请求,一般请求参数会类似下面:
query_hash: a5164aed103f24b03e7b7747a2d94e3cvariables: {"id":"1664922478","first":12,"after":"AQBJ8AGqCb5c9rO-dl2Z8ojZW12jrFbYZHxJKC1hP-nJKLtedNJ6VHzKAZtAd0oeUfgJqw8DmusHbQTa5DcoqQ5E3urx0BH9NkqZFePTP1Ie7A"}
— id 表示用户 id,可在 html 中的 sharedData 中获取– first 表示初始时获取多少条记录,好像蕞多是 50– after 表示分页游标,记录了分页获取的位置
当然 variables 部分里面的参数根据请求的 api 不同而可能不同 ,这里只列出与分页相关的参数。
分页请求参数首先是从 html 中的 sharedData 中获取的:
# 网页页面信息 page_info = js_data["entry_data"]["ProfilePage"][0]["graphql"]["user"]["edge_owner_to_timeline_media"]['page_info'] # 下一页的索引值AQCSnXw1JsoV6LPOD2Of6qQUY7HWyXRc_CBSMWB6WvKlseC-7ibKho3Em0PEG7_EP8vwoXw5zwzsAv_mNMR8yX2uGFZ5j6YXdyoFfdbHc6942w cursor = page_info['end_cursor'] # 是否有下一页 flag = page_info['has_next_page']
end_cursor 即为 after 的值,has_next_page 检测是否有下一页如果是有下一页,可进行弟一次分页数据请求,弟一次分页请求的响应数据回来之后,id,first 的值不用变,after 的值变为响应数据中 page_info 中 end_cursor 的值,再构造 variables,连同 query_hash 发起再下一页的请求再判断响应数据中的 page_info 中 has_next_page 的值,循环下去,可拿完全部数据。若不想拿完,可利用响应数据中的 edge_owner_to_timeline_media 中的 count 值来做判断,该值表示用户总共有多少媒体
视频帖子和图片帖子数据结构不一样,注意判断响应数据中的 is_video 字段如果是用一个 ins 账号去采集的话,只要请求头的 cookie 中带上合法且未过期的 session_id,可直接访问接口,无需计算签名。蕞直接的做法是:打开浏览器,登录 instagram 后,F12 查看 xhr 请求,将 request header 中的 cookie 复制过来使用即可,向下面:
headers = { 'user-agent': 'Mozilla/5.0 AppleWebKit/537.36 Chrome/68.0.3440.106 Safari/537.36', 'cookie': 'mid=XLaW9QAEAAH0WaPDCeY490qeeNlA; csrftoken=IgcP8rj0Ish5e9uHNXhVEsTId22tw8VE; ds_user_id=22859524403; sessionid=22859524403%3A74mdddCfCqXS7I%3A15; rur=PRN; urlgen="{\\"45.63.123.251\\": 20473}:1hGxr6:Phc4hR68jNts4Ig9FbrZRglG4YA"'}
在请求发出的时候带上类似上面的请求头错误日志记录表在 192.168.1.57 中 zk_flock 库克 ins_error_log,目前比较多 unknow ssl protocol 类型的错误,怀疑是爬取太快的原因,需要一个代理来切换
给出能运行的代码?:
# -*- coding:utf-8 -*-import requestsimport reimport jsonimport urllib.parseimport hashlibimport sysUSER_AGENT = 'Mozilla/5.0 AppleWebKit/537.36 Chrome/68.0.3440.106 Safari/537.36'BASE_URL = 'https://www.instagram.com'ACCOUNT_MEDIAS = "http://www.instagram.com/graphql/query/?query_hash=42323d64886122307be10013ad2dcc44&variables=%s"ACCOUNT_PAGE = 'https://www.instagram.com/%s'proxies = { 'http': 'http://127.0.0.1:1087', 'https': 'http://127.0.0.1:1087',}# 一次设置proxy的办法,将它设置在一次session会话中,这样就不用每次都在调用requests的时候指定proxies参数了# s = requests.session# s.proxies = {'http': '121.193.143.249:80'}def get_shared_data: """get window._sharedData from page,return the dict loaded by window._sharedData str """ if html: target_text = html else: header = generate_header response = requests.get target_text = response.text regx = r"\\s*.*\\s*<script.*?>.*_sharedData\\s*=\\s*;<\\/script>" match_result = re.match data = json.loads) return data# def get_rhx_gis:# """get the rhx_gis value from sharedData# """# share_data = get_shared_data# return share_data['rhx_gis']def get_account: """get the account info by username :param user_name: :return: """ url = get_account_link header = generate_header response = requests.get data = get_shared_data account = resolve_account_data return accountdef get_media_by_user_id: """get media info by user id :param id: :param count: :param max_id: :return: """ index = 0 medias = [] has_next_page = True while index <= count and has_next_page: varibles = json.dumps, 'first': count, 'after': str }, separators=) # 不指定separators的话key:value的:后会默认有空格,因为其默认separators为 url = get_account_media_link header = generate_header response = requests.get media_json_data = json.loads media_raw_data = media_json_data['data']['user']['edge_owner_to_timeline_media']['edges'] if not media_raw_data: return medias for item in media_raw_data: if index == count: return medias index += 1 medias.append) max_id = media_json_data['data']['user']['edge_owner_to_timeline_media']['page_info']['end_cursor'] has_next_page = media_json_data['data']['user']['edge_owner_to_timeline_media']['page_info']['has_next_page'] return mediasdef get_media_by_url: response = requests.get, proxies=proxies, headers=generate_header) media_json = json.loads return general_resolve_mediadef get_account_media_link: return ACCOUNT_MEDIAS % urllib.parse.quotedef get_account_link: return ACCOUNT_PAGE % user_namedef get_media_url: return media_url.rstrip + '/?__a=1'# def generate_instagram_gis:# rhx_gis = get_rhx_gis# gis_token = rhx_gis + ':' + varibles# x_instagram_token = hashlib.md5).hexdigest# return x_instagram_tokendef generate_header: # todo: if have session, add the session key:value to header header = { 'user-agent': USER_AGENT, } if gis_token: header['x-instagram-gis'] = gis_token return headerdef general_resolve_media: res = { 'id': media['id'], 'type': media['__typename'][5:].lower, 'content': media['edge_media_to_caption']['edges'][0]['node']['text'], 'title': 'title' in media and media['title'] or '', 'shortcode': media['shortcode'], 'preview_url': BASE_URL + '/p/' + media['shortcode'], 'comments_count': media['edge_media_to_comment']['count'], 'likes_count': media['edge_media_preview_like']['count'], 'dimensions': 'dimensions' in media and media['dimensions'] or {}, 'display_url': media['display_url'], 'owner_id': media['owner']['id'], 'thumbnail_src': 'thumbnail_src' in media and media['thumbnail_src'] or '', 'is_video': media['is_video'], 'video_url': 'video_url' in media and media['video_url'] or '' } return resdef resolve_account_data: account = { 'country': account_data['country_code'], 'language': account_data['language_code'], 'biography': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['biography'], 'followers_count': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['edge_followed_by']['count'], 'follow_count': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['edge_follow']['count'], 'full_name': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['full_name'], 'id': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['id'], 'is_private': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['is_private'], 'is_verified': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['is_verified'], 'profile_pic_url': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['profile_pic_url_hd'], 'username': account_data['entry_data']['ProfilePage'][0]['graphql']['user']['username'], } return accountaccount = get_accountresult = get_media_by_user_idmedia = get_media_by_urlprint)print
封装成库了!
关注+转发。私信小编:“爬虫”了解更多爬虫知识。
海外精品引流脚本–最强海外引流
唯一TG:https://t.me/Facebook181818
更多海外引流脚本方案
如果你需要脚本演示、部署咨询或海外获客方案,可以通过下面入口继续查看。
