def running(self, page_url: str) -> Dict: """执行Alexa爬虫 :param page_url: 目标网站地址 :return: 字典格式的Alexa数据 """ # 计算实际请求的Alexa页面Url actual_url = "https://www.alexa.com/siteinfo/{0}".format(page_url) self.console("抓取开始,实际请求Url:" + actual_url) item = {} # 执行请求 response_text = tool.try_request(actual_url) if not response_text: self.log("网站Url:" + page_url + "|请求失败,网络可能出现问题") return item bs = BeautifulSoup(response_text, "lxml") # 读取网站的Alexa排名 if label := bs.select_one( "#card_rank > section.rank > div.rank-global > div:nth-child(1) > div:nth-child(2) > p.big.data" ): item["alexa_rank"] = tool.extract.number(label.text)
def running(self, user_name: str, since_timestamp: int, until_timestamp: int) -> List[Dict]: """执行Facebook账号推文爬虫 :param user_name: Facebook账号主页名称(可以通过get_facebook_user_name获取) :param since_timestamp: 抓取时间范围的左侧边界(最早时间戳) :param until_timestamp: 抓取时间范围的右侧边界(最晚时间戳) :return: 推文信息列表 """ # 初始化爬虫实例的变量 self.user_name = user_name # 记录当前正在抓取的Facebook账号 self.earliest_post = None # 时间戳格式的追溯最早推文时间 post_list = [] # 推文列表 is_first_page = True # 是否正在处理账号首页 timeline_cursor = None # 下一次请求的Url参数 user_page_id = None # 下一次请求的Url参数 # 计算Facebook账号帖子页的Url page_url = "https://www.facebook.com/pg/{}/posts/".format(user_name) self.console("开始抓取,目标账号帖子页Url:" + page_url) for page in range(200): # 处理帖子首页的情况 if is_first_page: # 请求目标Facebook账号的帖子页 response_text = tool.try_request(url=page_url, headers=self.headers, proxies=self.proxies) if not response_text: self.log("抓取结束:帖子首页请求结果为空(账号不存在/账号已失效)") return post_list # 读取用户的page_id(用作Ajax的请求参数) if pattern := re.search(r"(?<=page_id=)\d+(?=&)", response_text): user_page_id = pattern.group() else: self.log("抓取结束:读取Facebook账号的page_id失败(账号不存在/账号已失效)") return post_list # 解析目标Facebook账号的帖子页:生成etree解析器 parser = etree.HTML(response_text) # 解析目标Facebook账号的帖子页:获取包含反馈数据的Json格式数据 feedback_json = None soup = BeautifulSoup(response_text, "lxml") if label := soup.select_one("body > script:nth-child(4)"): if pattern := re.search(r"{.*}", str(label)): try: feedback_json = self.extract_feedback( json.loads( pattern.group())["pre_display_requires"]) except (json.JSONDecodeError, KeyError): pass
def running(self, user_id: str) -> List[Dict]: """执行微博账号信息爬虫 :param user_id: 微博账号的ID :return: Json格式的Twitter账号数据 """ self.user_id = user_id actual_url = "https://m.weibo.cn/api/container/getIndex?type=uid&value=" + str( user_id) self.console("开始抓取,实际请求的Url:" + actual_url) item = {} response_text = tool.try_request( actual_url, headers={"User-Agent": tool.static.USER_AGENT["Win10_Chrome83"]}) if not response_text: self.log("微博账号:" + str(user_id) + "|账号信息API请求失败") try: response_json = json.loads( response_text.content.decode("UTF-8", errors="ignore")) # 读取微博账号的粉丝数 item["followers"] = response_json["data"]["userInfo"][ "followers_count"] # 读取微博账号微博的Domain值 for tab in response_json["data"]["tabsInfo"]["tabs"]: if tab["id"] == 2: item["domain"] = tab["containerid"][:6] break except (json.decoder.JSONDecodeError, KeyError): self.log("微博账号:" + str(user_id) + "|账号信息API内容格式异常") return [item]
# 处理非帖子首页的情况(使用Ajax请求获取的下拉页面) else: self.console("第{}次下拉,当前追溯时间:{}".format( page, self.earliest_post_time)) # 检查Ajax请求所需参数是否正常 if not timeline_cursor or not user_page_id: self.log( "抓取结束:Ajax请求参数未能正常获取(timeline_cursor/user_page_id为空)") return post_list # 计算需要请求的Ajax地址并执行请求 ajax_url = self.make_ajax_url(timeline_cursor=timeline_cursor, user_page_id=user_page_id) response_text = tool.try_request(url=ajax_url, headers=self.headers, proxies=self.proxies) if not response_text: self.log("抓取结束:Ajax请求结果为空(出现异常/已抓取该账号的所有推文)") return post_list # 解析Ajax请求结果 parser, feedback_json = None, None if pattern := re.search(r"{.*}", response_text): try: response_json = json.loads(pattern.group()) parser = etree.HTML( response_json["domops"][0][3]["__html"]) feedback_json = self.extract_feedback( response_json["jsmods"]["pre_display_requires"]) except (json.JSONDecodeError, KeyError):
def running(self, user_id: str, since_timestamp: int, until_timestamp: int) -> List[Dict]: """执行微博推文爬虫 :param user_id: 微博账号ID :param since_timestamp: 抓取时间范围的右侧边界(最早时间戳) :param until_timestamp: 抓取时间范围的左侧边界(最晚时间戳) :return: 微博推文数据列表 """ # 初始化爬虫实例的变量 self.user_id = user_id self.console("抓取开始,目标微博账号ID:" + user_id) post_list = [] # 推文列表 maybe_finish = False # 是否已包含可能的置顶推文 for page in range(1, 1001): # 请求参数中page=0和page=1均返回第1页(即page=1为第1页) # 计算实际请求的Ajax的Url actual_url = ("https://m.weibo.cn/api/container/getIndex?" + "type=uid" + "&value=" + user_id + "&containerid=" + self._DEFAULT_DOMAIN + user_id + "&page=" + str(page)) self.console("抓取开始,第" + str(page) + "次下拉,实际请求Url:" + actual_url) # 对Ajax执行请求 response_text = tool.try_request( actual_url, headers={"User-Agent": tool.static.USER_AGENT["Win10_Chrome"]}) if not response_text: self.log("抓取结束:Ajax请求结果为空") return post_list try: response_json = json.loads(response_text)["data"]["cards"] except (json.JSONDecodeError, KeyError): self.log("抓取结束:Ajax请求结果的数据格式异常(数据并非Json格式数据)") return post_list # 遍历解析推文数据 for content in response_json[1:]: item = {} # 解析推文数据 try: content = content["mblog"] # 微博账号内容信息全在这个标签之后 item["time"] = self.count_time_date(content["created_at"]) item["post_id"] = content["id"] item["post_bid"] = content["bid"] item["text"] = content["text"].replace("\n", ";") item["likes"] = content["attitudes_count"] item["comments"] = content["comments_count"] item["reposts"] = content["reposts_count"] item["if_repost"] = ("retweeted_status" in content) except KeyError: self.log("抓取结束:Ajax请求结果的数据格式异常(Json格式数据结构异常)") return post_list if "time" not in item or not item[ "time"] or until_timestamp <= item["time"]: # 如果当前推文晚于抓取时间范围的右侧边界则跳过当前推文 continue elif since_timestamp <= item["time"] < until_timestamp: # 如果当前推文处于抓取时间范围内则存储当前推文 post_list.append(item) elif maybe_finish: # 如果当前推文早于抓取时间范围的左侧边界且已考虑可能的置顶推文则结束当前抓取 return post_list else: # 如果当前推文早于抓取时间范围的左侧边界且未考虑可能的置顶推文则标记已考虑置顶推文 maybe_finish = True time.sleep(5) return post_list