From 69cb7b2d2c147c24f72f94384d83f10d70f6b1c9 Mon Sep 17 00:00:00 2001 From: libinbin Date: Tue, 4 Nov 2025 08:24:12 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BB=8A=E6=97=A5=E5=A4=B4?= =?UTF-8?q?=E6=9D=A1=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/jinritoutiaofinder.py | 179 ++++++++++++++++++++++++++++++++++ social_mapper.py | 114 +++++++++++++++++++++- 2 files changed, 289 insertions(+), 4 deletions(-) create mode 100644 modules/jinritoutiaofinder.py diff --git a/modules/jinritoutiaofinder.py b/modules/jinritoutiaofinder.py new file mode 100644 index 0000000..f92c99f --- /dev/null +++ b/modules/jinritoutiaofinder.py @@ -0,0 +1,179 @@ +from __future__ import print_function + +import os +import sys +import traceback +from time import sleep + +from bs4 import BeautifulSoup +from pyvirtualdisplay import Display +from selenium import webdriver +from selenium.webdriver.common.keys import Keys +from selenium.webdriver.firefox.options import Options + + +class Jinritoutiaofinder(object): + timeout = 10 + + def __init__(self, showbrowser): + if sys.platform == "darwin": + display = Display(visible=0, size=(1600, 1024)) + display.start() + opts = Options() + if not showbrowser: + os.environ['MOZ_HEADLESS'] = '1' + opts.headless = True + else: + opts.headless = False + firefoxprofile = webdriver.FirefoxProfile() + firefoxprofile.set_preference("permissions.default.desktop-notification", 1) + firefoxprofile.set_preference("dom.webnotifications.enabled", 1) + firefoxprofile.set_preference("dom.push.enabled", 1) + self.driver = webdriver.Firefox(firefox_profile=firefoxprofile, options=opts) + + self.driver.implicitly_wait(15) + self.driver.delete_all_cookies() + + def doLogin(self, username, password): + try: + self.driver.get("https://sso.toutiao.com/login/") + self.driver.execute_script('localStorage.clear();') + sleep(3) + + print("\n[+] Jinritoutiao Login Page loaded successfully [+]") + + # 切换到密码登录 + try: + password_login_btn = self.driver.find_element_by_xpath("//div[contains(text(), '密码登录')]") + password_login_btn.click() + sleep(2) + except: + print("[-] Could not find password login button [-]") + + # 输入用户名 + try: + username_field = self.driver.find_element_by_xpath("//input[@name='username']") + username_field.send_keys(username) + sleep(1) + except: + print("[-] Could not find username field [-]") + traceback.print_exc() + return False + + # 输入密码 + try: + password_field = self.driver.find_element_by_xpath("//input[@name='password']") + password_field.send_keys(password) + sleep(1) + except: + print("[-] Could not find password field [-]") + traceback.print_exc() + return False + + # 点击登录按钮 + try: + login_btn = self.driver.find_element_by_xpath("//button[contains(text(), '登录')]") + login_btn.click() + sleep(5) + except: + print("[-] Could not find login button [-]") + traceback.print_exc() + return False + + # 检查登录是否成功 + if "toutiao.com" in self.driver.current_url: + print("[+] Jinritoutiao Login Success [+]\n") + return True + else: + print("[-] Jinritoutiao Login Failed [-]\n") + return False + + except Exception as e: + print("[-] Error during Jinritoutiao login: {}".format(e)) + traceback.print_exc() + return False + + def getJinritoutiaoProfiles(self, first_name, last_name): + try: + # 搜索用户 + url = "https://so.toutiao.com/search?keyword={}+{}".format(first_name, last_name) + self.driver.get(url) + sleep(3) + + searchresponse = self.driver.page_source.encode('utf-8') + soupParser = BeautifulSoup(searchresponse, 'html.parser') + picturelist = [] + + # 查找用户个人资料 + # 注意:今日头条的HTML结构可能会变化,需要根据实际情况调整 + for element in soupParser.find_all('div', {'class': 'user-info'}): + try: + link = element.find('a')['href'] + if not link.startswith("http"): + link = "https://www.toutiao.com" + link + + # 获取头像 + img_element = element.find('img') + if img_element: + profilepic = img_element['src'] + picturelist.append([link, profilepic, 1.0]) + + except Exception as e: + continue + + return picturelist + + except Exception as e: + print('[-] Error on line {}: {}'.format(sys.exc_info()[-1].tb_lineno, e)) + return [] + + def sendFriendRequest(self, profile_link): + try: + self.driver.get(profile_link) + sleep(3) + + # 查找并点击关注按钮 + # 注意:按钮的类名可能会变化 + follow_btn = self.driver.find_element_by_xpath("//button[contains(text(), '关注')]") + follow_btn.click() + sleep(2) + + print("[+] Friend request sent successfully to {}".format(profile_link)) + return True + + except Exception as e: + print("[-] Error sending friend request: {}".format(e)) + traceback.print_exc() + return False + + def sendMessage(self, profile_link, message): + try: + self.driver.get(profile_link) + sleep(3) + + # 查找并点击消息按钮 + # 注意:按钮的类名可能会变化 + message_btn = self.driver.find_element_by_xpath("//button[contains(text(), '发消息')]") + message_btn.click() + sleep(2) + + # 输入消息 + message_field = self.driver.find_element_by_xpath("//textarea[@placeholder='请输入消息']") + message_field.send_keys(message) + sleep(1) + + # 发送消息 + send_btn = self.driver.find_element_by_xpath("//button[contains(text(), '发送')]") + send_btn.click() + sleep(2) + + print("[+] Message sent successfully to {}".format(profile_link)) + return True + + except Exception as e: + print("[-] Error sending message: {}".format(e)) + traceback.print_exc() + return False + + def kill(self): + self.driver.quit() \ No newline at end of file diff --git a/social_mapper.py b/social_mapper.py index c0089c3..341bc5a 100644 --- a/social_mapper.py +++ b/social_mapper.py @@ -26,6 +26,7 @@ from modules import twitterfinder from modules import vkontaktefinder from modules import weibofinder +from modules import jinritoutiaofinder assert sys.version_info >= (3,), "Only Python 3 is currently supported." @@ -65,6 +66,10 @@ global pinterest_password pinterest_username = "" pinterest_password = "" +global jinritoutiao_username +global jinritoutiao_password +jinritoutiao_username = "" +jinritoutiao_password = "" global showbrowser @@ -94,6 +99,8 @@ class Person(object): doubanimage = "" pinterest = "" pinterestimage = "" + jinritoutiao = "" + jinritoutiaoimage = "" def __init__(self, first_name, last_name, full_name, person_image): self.first_name = first_name @@ -113,6 +120,88 @@ def __init__(self, full_name, profile_link, image_link): self.image_link = image_link +def fill_jinritoutiao(peoplelist): + JinritoutiaofinderObject = jinritoutiaofinder.Jinritoutiaofinder(showbrowser) + JinritoutiaofinderObject.doLogin(jinritoutiao_username, jinritoutiao_password) + if args.waitafterlogin: + input("Press Enter to continue after verifying you are logged in...") + + count = 1 + ammount = len(peoplelist) + for person in peoplelist: + if args.vv == True or args.debug == True: + print("Jinritoutiao Check %i/%i : %s" % (count, ammount, person.full_name)) + else: + sys.stdout.write( + "\rJinritoutiao Check %i/%i : %s " % (count, ammount, person.full_name)) + sys.stdout.flush() + count = count + 1 + + if person.person_image: + try: + target_image = face_recognition.load_image_file(person.person_image) + target_encoding = face_recognition.face_encodings(target_image)[0] + profilelist = JinritoutiaofinderObject.getJinritoutiaoProfiles(person.first_name, person.last_name) + if args.debug == True: + print(profilelist) + except: + continue + else: + continue + + early_break = False + updatedlist = [] + for profilelink, profilepic, distance in profilelist: + try: + os.remove("potential_target_image.jpg") + except: + pass + if early_break: + break + image_link = profilepic + + cookies = JinritoutiaofinderObject.getCookies() + if image_link: + try: + headers = { + 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14'} + response = requests.get(image_link, cookies=cookies, headers=headers, stream=True) + with open('potential_target_image.jpg', 'wb') as out_file: + response.raw.decode_content = True + shutil.copyfileobj(response.raw, out_file) + del response + potential_target_image = face_recognition.load_image_file("potential_target_image.jpg") + try: + potential_target_encoding = face_recognition.face_encodings(potential_target_image)[0] + except: + continue + results = face_recognition.face_distance([target_encoding], potential_target_encoding) + for result in results: + if args.mode == "fast": + if result < threshold: + person.jinritoutiao = encoding.smart_str(profilelink, encoding='ascii', errors='ignore') + person.jinritoutiaoimage = encoding.smart_str(image_link, encoding='ascii', errors='ignore') + if args.vv == True: + print("\tMatch found: " + person.full_name) + print("\tJinritoutiao: " + person.jinritoutiao) + early_break = True + break + else: + updatedlist.append([result, profilelink, image_link]) + except: + continue + + if not args.mode == "fast": + if len(updatedlist) > 0: + updatedlist.sort() + if updatedlist[0][0] < threshold: + person.jinritoutiao = encoding.smart_str(updatedlist[0][1], encoding='ascii', errors='ignore') + person.jinritoutiaoimage = encoding.smart_str(updatedlist[0][2], encoding='ascii', errors='ignore') + if args.vv == True: + print("\tMatch found: " + person.full_name) + print("\tJinritoutiao: " + person.jinritoutiao) + + JinritoutiaofinderObject.kill() def fill_facebook(peoplelist): FacebookfinderObject = facebookfinder.Facebookfinder(showbrowser) FacebookfinderObject.doLogin(facebook_username, facebook_password) @@ -1420,6 +1509,13 @@ def loadPage(client, url, data=None): doubanwritestring = '"%s","%s","%s","%s","%s","%s"\n' % ( person.first_name, person.last_name, person.full_name, email, person.douban, person.doubanimage) filewriterdouban.write(doubanwritestring) + if args.a == True or args.tt == True or args.format == "socialmapper": + writestring = writestring + '"%s",' % (person.jinritoutiao) + if person.jinritoutiao != "" and args.email is not None: + if email != "Error": + jinritoutiaowritestring = '"%s","%s","%s","%s","%s","%s"\n' % ( + person.first_name, person.last_name, person.full_name, email, person.jinritoutiao, person.jinritoutiaoimage) + filewriterjinritoutiao.write(jinritoutiaowritestring) writestring = writestring[:-1] filewriter.write(writestring) @@ -1444,6 +1540,8 @@ def loadPage(client, url, data=None): terminalstring = terminalstring + "\tWeibo: " + person.weibo + "\n" if person.douban != "": terminalstring = terminalstring + "\tDouban: " + person.douban + "\n" + if person.jinritoutiao != "": + terminalstring = terminalstring + "\tJinritoutiao: " + person.jinritoutiao + "\n" if terminalstring != "": print(person.full_name + "\n" + terminalstring) @@ -1493,6 +1591,11 @@ def loadPage(client, url, data=None): filewriterdouban.close() except: pass +try: + if filewriterjinritoutiao: + filewriterjinritoutiao.close() +except: + pass # Code for generating HTML file htmloutputfilename = "SM-Results/" + args.input.replace("\"", "").replace("/", "-") + "-social-mapper.html" @@ -1633,10 +1736,10 @@ def loadPage(client, url, data=None): """ foot = "" -header = """
+header = """
- - + + @@ -1647,6 +1750,7 @@ def loadPage(client, url, data=None): + """ filewriter.write(css) @@ -1671,13 +1775,15 @@ def loadPage(client, url, data=None): "" \ "" \ "" \ + "" \ "" \ "" % ( local_image_link, local_image_link, person.full_name, person.linkedin, person.linkedinimage, person.linkedin, person.facebook, person.facebookcdnimage, person.facebook, person.twitter, person.twitterimage, person.twitter, person.instagram, person.instagramimage, person.instagram, person.pinterest, person.pinterestimage, person.pinterest, person.vk, person.vkimage, person.vk, - person.weibo, person.weiboimage, person.weibo, person.douban, person.doubanimage, person.douban) + person.weibo, person.weiboimage, person.weibo, person.douban, person.doubanimage, person.douban, + person.jinritoutiao, person.jinritoutiaoimage, person.jinritoutiao) filewriter.write(body) filewriter.write(foot)
PhotoNamePhotoName LinkedIn Facebook TwitterVKontakte Weibo DoubanJinritoutiao
VKontakte:
%s
Weibo:
%s
Douban:
%s
Jinritoutiao:
%s