LinkedIn 自動消息發送工具

LinkedIn 自動消息發送工具說明文檔

一、項目概述

本項目是一個基于 Python 的自動化工具,用于批量向指定 LinkedIn 用戶發送消息。
核心功能包括:

  • 讀取消息模板和 URL 列表;
  • 使用瀏覽器模擬操作,自動發送 LinkedIn 消息;
  • 使用 Redis 緩存已發送的 URL,避免重復發送;
  • 支持命令行參數配置,靈活控制運行行為。

二、項目結構

├── main.py                          # 主程序入口
├── linkedin_cat/
│   └── message.py                   # LinkedinMessage 類,負責發送消息
├── cookies.json                     # LinkedIn 登錄 Cookie(用戶自備)
├── message.txt                      # 消息模板文件(用戶自備)
├── urls.txt                         # 目標 LinkedIn 用戶 URL 列表(用戶自備)
├── log.txt                          # 運行日志文件(自動生成)

三、依賴環境

  • Python 3.6+
  • 第三方庫:
    • redis
    • selenium(LinkedinMessage 類內部使用)
  • Redis 服務(用于緩存已發送的 URL)

可使用以下命令安裝依賴:

pip install redis selenium

四、使用方法

4.1 準備文件

  • cookies.json:登錄 LinkedIn 后,從瀏覽器中導出的 Cookie 信息(JSON 格式)。
  • message.txt:要發送的消息內容(純文本)。
  • urls.txt:每行一個目標 LinkedIn 用戶主頁 URL。

4.2 運行命令

python main.py cookies.json message.txt urls.txt "button_class" [--可選參數]
參數說明:
參數說明
cookiesLinkedIn 登錄 Cookie 文件路徑
message消息模板文件路徑
urls目標 URL 列表文件路徑
button_classLinkedIn 頁面“發送消息”按鈕的 class 屬性值(需自行查找)
可選參數:
參數默認值說明
--headlessFalse無頭模式運行(不打開瀏覽器窗口)
--redis-hostlocalhostRedis 地址
--redis-port6379Redis 端口
--redis-db0Redis 數據庫編號
--redis-passwordNoneRedis 密碼
--redis-max-connections10Redis 最大連接數
--max-urls100最多處理的 URL 數量

4.3 示例

python main.py cookies.json message.txt urls.txt "ieSHXhFfVTxQfadOJdXYOIDuVKsBXgPtjNxI" --headless --max-urls 50

五、核心邏輯說明

5.1 Redis 緩存機制

  • 每個 URL 作為 Redis 的 key;
  • Value 為該 URL 最近一次成功發送消息時的時間戳;
  • 若某 URL 在 30 天內已發送過,則跳過;
  • 若某 URL 的 Value 為小于 100 的整數,則視為“黑名單”,永久跳過。

5.2 消息發送流程

  1. 讀取消息模板和 URL 列表;
  2. 初始化 Redis 連接;
  3. 遍歷 URL 列表:
    • 若 URL 不存在于 Redis 中,則直接發送消息;
    • 若 URL 存在于 Redis 中,檢查時間戳:
      • 若超過 30 天,則重新發送;
      • 否則跳過;
  4. 每次成功發送后,更新 Redis 中的時間戳。

5.3 日志記錄

  • 所有運行日志寫入 log.txt
  • 日志格式:時間 - 級別 - 消息

六、注意事項

  • LinkedIn 反爬機制:頻繁操作可能導致賬號被限制,請合理設置發送間隔和數量;
  • Cookie 有效性:Cookie 可能會過期,需定期更新;
  • 按鈕 class 值:LinkedIn 頁面結構可能會變化,需定期更新按鈕 class 值;
  • Redis 持久化:建議開啟 Redis 持久化,避免重啟后數據丟失。

七、常見問題

問題解決方案
程序運行后立即退出檢查 Cookie 是否有效,或按鈕 class 值是否正確
提示“Skipping URL”該 URL 已發送過,且未超過 30 天
日志中出現異常信息檢查 Redis 是否正常運行,網絡是否暢通

八、后續擴展建議

  • 支持多賬號輪詢發送;
  • 支持發送間隔隨機化,降低風控風險;
  • 支持消息模板變量替換(如用戶名);
  • 支持 Web 管理界面,可視化配置任務。
import redis
import argparse
from linkedin_cat.message import LinkedinMessage
import json
import time
import datetimeimport logging# 創建日志記錄器
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)  # 設置日志級別為INFO# 創建文件處理器,將日志寫入到 log_email.txt 文件
file_handler = logging.FileHandler('log.txt', encoding='utf-8')
file_handler.setLevel(logging.INFO)  # 設置文件處理器的日志級別# 創建日志格式化器
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)  # 將格式化器添加到文件處理器# 將文件處理器添加到日志記錄器
logger.addHandler(file_handler)def is_timestamp_difference_greater_than_months(float_value, months_in_seconds):"""判斷時間戳與當前時間的差異是否大于指定的秒數(表示的月數):param float_value: 浮點數時間戳:param months_in_seconds: 以秒為單位表示的月數:return: 如果時間差大于指定的秒數,返回True;否則返回False"""# 將浮點數時間戳轉換為整數timestamp = int(float_value)# 獲取當前時間戳current_timestamp = int(time.time())# 計算時間差(單位:秒)time_difference = current_timestamp - timestamp# 判斷時間差是否大于指定的秒數if time_difference > months_in_seconds:print('可以發送.....')return Trueelse:return Falseclass RedisHelper:def __init__(self, host='localhost', port=6379, db=0, password=None, max_connections=10):self.host = hostself.port = portself.db = dbself.password = passwordself.max_connections = max_connectionsself.pool = redis.ConnectionPool(host=self.host, port=self.port, db=self.db, password=self.password, max_connections=self.max_connections)self.conn = redis.Redis(connection_pool=self.pool)def set(self, key, value, ex=None, px=None, nx=False, xx=False):self.conn.set(key, value, ex=ex, px=px, nx=nx, xx=xx)def get(self, key):return self.conn.get(key)@classmethoddef get_key_value_timestamp(cls, key, host='localhost', port=6379, db=0, password=None, max_connections=10):# 創建 RedisHelper 實例redis_helper = cls(host=host, port=port, db=db, password=password, max_connections=max_connections)# 從 Redis 中獲取值value = redis_helper.get(key)if value is not None:try:# 將 value 從 bytes 轉換為 int(假設存儲的是 Unix 時間戳)# 將字節字符串解碼為普通字符串decoded_string = value.decode('utf-8')# 將字符串轉換為浮點數(因為時間戳可能包含小數部分)float_value = float(decoded_string)# 如果需要整數部分,可以將浮點數轉換為整數timestamp = int(float_value)# 將時間戳轉換為 datetime 對象timestamp_datetime = datetime.datetime.fromtimestamp(timestamp)# 獲取當前時間current_datetime = datetime.datetime.now()# 計算時間差time_difference = current_datetime - timestamp_datetime# 判斷時間差是否大于 4 周(4 周 = 4 * 7 天)if time_difference > datetime.timedelta(weeks=4):return {"key": key,"value": timestamp,"resend": True,"time_difference": time_difference}else:return {"key": key,"value": timestamp,"resend": False,"time_difference": time_difference}except ValueError:return {"key": key,"value": value,"resend": False,"error": "value is not a valid timestamp"}else:return {"key": key,"resend": False,"error": "key not found or value is None"}def get_message(message_file_path):with open(message_file_path, "r", encoding="utf8") as f:message = f.read()return messagedef read_urls_list(urls_file_path):with open(urls_file_path, "r", encoding="utf8") as f:urls_list = f.readlines()urls_list = [url.strip() for url in urls_list]return urls_listdef send_messages(urls_list, message, storage_helper, bot, max_urls):# 限制 urls_list 的長度不超過 max_urlsurls_list = urls_list[:max_urls]for raw_url in urls_list:url = raw_url# url = raw_url.split('?')[0] + '/'if storage_helper.get(url):value = storage_helper.get(url)decoded_string = value.decode('utf-8')# 將字符串轉換為浮點數(因為時間戳可能包含小數部分)float_value = float(decoded_string)# 如果需要整數部分,可以將浮點數轉換為整數timestamp = int(float_value)if timestamp < 100:print(f"Skipping URL {url} as it has already been marked.")continueresult = is_timestamp_difference_greater_than_months(float_value,30*86400)if result:print(">>>>>",timestamp, url)result = bot.send_single_request(raw_url, message)if result != 'fail':storage_helper.set(url, time.time())print(f"Message sent to {url} and URL marked as processed.")continue# data = storage_helper.get_key_value_timestamp(url)# print(data)# if data.get('resend'):#     result = bot.send_single_request(raw_url, message)#     if result != 'fail':#         storage_helper.set(url, time.time())#         print(f"Message sent to {url} and URL marked as processed.")else:print(f"Skipping URL {url} as it has already been processed.")continueelse:result = bot.send_single_request(raw_url, message)if result != 'fail':storage_helper.set(url, time.time())print(f"Message sent to {url} and URL marked as processed.")def main():parser = argparse.ArgumentParser(description='Send LinkedIn messages to a list of URLs.')parser.add_argument('cookies', type=str, help='Path to the LinkedIn cookies JSON file (e.g., "cookies.json").')parser.add_argument('message', type=str, help='Path to the message file (e.g., "message.txt").')parser.add_argument('urls', type=str, help='Path to the URLs file (e.g., "urls.txt").')parser.add_argument('button_class', type=str, help="""Message Button Class: ieSHXhFfVTxQfadOJdXYOIDuVKsBXgPtjNxI (eg:<button aria-label="Invite XXXX to connect" id="ember840"class="artdeco-button artdeco-button--2artdeco-button--primary ember-view ieSHXhFfVTxQfadOJdXYOIDuVKsBXgPtjNxI"type="button">)""")parser.add_argument('--headless', action='store_true',help='Run the bot in headless mode (without opening a browser window).')parser.add_argument('--redis-host', type=str, default='localhost', help='Redis host (default: localhost)')parser.add_argument('--redis-port', type=int, default=6379, help='Redis port (default: 6379)')parser.add_argument('--redis-db', type=int, default=0, help='Redis database (default: 0)')parser.add_argument('--redis-password', type=str, default=None, help='Redis password (default: None)')parser.add_argument('--redis-max-connections', type=int, default=10, help='Redis max connections (default: 10)')parser.add_argument('--max-urls', type=int, default=100, help='Maximum number of URLs to process (default: 100)')args = parser.parse_args()message = get_message(args.message)urls_list = read_urls_list(args.urls)bot = LinkedinMessage(args.cookies, args.headless, button_class=args.button_class)storage_helper = RedisHelper(host=args.redis_host,port=args.redis_port,db=args.redis_db,password=args.redis_password,max_connections=args.redis_max_connections)send_messages(urls_list, message, storage_helper, bot, args.max_urls)if __name__ == "__main__":main()

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/94275.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/94275.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/94275.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

新的 macOS 安裝程序聲稱能夠快速竊取數據,并在暗網上銷售

一種新型 macOS 信息竊取惡意軟件&#xff0c;被命名為 Mac.c&#xff0c;已成為地下惡意軟件即服務 (MaaS) 生態系統中強大的競爭者。 Mac.c 由使用化名“mentalpositive”的威脅行為者公開開發&#xff0c;是臭名昭著的 Atomic MacOS Stealer (AMOS) 的簡化衍生品&#xff0…

我的小灶坑

最近在寫項目 有時候希望有個人能跟我一起來寫 這樣子交流中也能有很多新的想法 但也并不是都是優點 因為我現在不是處于對這個項目的每個步驟都很熟悉的階段。 我覺得一個人從零到一確實能捋順不少 但是我在做項目的時候發現自己經常容易被細節的部分牽制 比如說一個按鈕的樣式…

6.4 Element UI 中的 <el-table> 表格組件

一、 核心組成與基本結構Element UI 的表格主要由以下幾個核心部分構成&#xff1a;<el-table>: 表格的根容器&#xff0c;負責管理數據、選擇、排序、分頁集成等全局狀態。<el-table-column>: 定義表格的一列。表格的列結構由一個或多個 <el-table-column> …

Linux 軟件編程(十一)網絡編程:TCP 機制與 HTTP 協議

五、TCP 進階機制&#xff08;一&#xff09;TCP 頭部標志位TCP 頭部的標志位是控制通信行為的 “開關”&#xff0c;常用標志位功能&#xff1a;標志位含義典型場景SYN請求建立連接三次握手第一步&#xff0c;發起連接請求ACK響應報文確認回復對方&#xff0c;確認已收到數據P…

[element-plus] el-table在行單擊時獲取行的index

el-table中添加 row-class-name&#xff0c;綁定row-click事件 <el-table:data"list":row-class-name"tableRowClassName"row-click"handleRowClick" > </el-table>給el-table中的每個row對象里添加index屬性 tableRowClassName({…

真實應急響應案例記錄

成功溯源的應急背景事件背景&#xff1a;服務器被植入博彩黑鏈入侵排查查看日志&#xff1a;發現Struts2漏洞利用痕跡通過process monitor工具監控Web進程(java.exe),發現執行了以下命令:攻擊入侵者服務器查看Web日志,可發現攻擊者的的Ip地址61.139.77.xx (四川省成都市 61.139…

RAG學習(五)——查詢構建、Text2SQL、查詢重構與分發

檢索優化&#xff08;二&#xff09; 一、查詢構建 在前面的章節中&#xff0c;我們探討了如何通過向量嵌入和相似度搜索來從非結構化數據中檢索信息。然而&#xff0c;在實際應用中&#xff0c;我們常常需要處理更加復雜和多樣化的數據&#xff0c;包括結構化數據&#xff0…

【typenum】 28 數組長度和二進制數的位數(Len)

一、源碼 這段代碼實現了一個類型級別的長度計算系統&#xff0c;用于在編譯時計算數組長度和二進制數的位數。 定義&#xff08;type_operators.rs&#xff09; /// A **type operator** that gives the length of an Array or the number of bits in a UInt. #[allow(clippy:…

【Docker項目實戰】使用Docker部署Hibiscus.txt簡單日記工具

【Docker項目實戰】使用Docker部署Hibiscus.txt簡單日記工具一、Hibiscus介紹1.1 Hibiscus簡介1.2 主要特點二、本次實踐規劃2.1 本地環境規劃2.2 本次實踐介紹三、本地環境檢查3.1 檢查Docker服務狀態3.2 檢查Docker版本3.3 檢查docker compose 版本四、拉取鏡像五、部署Hibis…

openharmony之啟動恢復子系統詳解

OpenHarmony的啟動恢復子系統負責整個系統的啟動流程&#xff0c;其中init進程是整個系統啟動的第一個用戶態進程&#xff08;PID1&#xff09;&#xff0c;承擔著系統初始化的核心職責 &#x1f3af; 目錄結構 &#x1f4cb; 理論基礎&#x1f50d; 源碼結構分析?? 配置體系…

Jenkins + SonarQube 從原理到實戰四:Jenkins 與 Gerrit 集成并實現自動任務

前言 前面我們已經部署了 SonarQube&#xff0c;并加入了 sonar-cxx 插件&#xff0c;實現了 C/C 代碼掃描&#xff0c;同時打通了 Windows AD 域&#xff0c;實現了 AD 用戶登錄與權限管控。 原計劃本篇&#xff08;第四篇&#xff09;完成 Jenkins Gerrit Sonar 的 CI 部分…

基于Spring Boot與Redis的電商場景面試問答解析

基于Spring Boot與Redis的電商場景面試問答解析 第一輪&#xff1a;基礎問題 面試官&#xff1a; 你好小C&#xff0c;今天我們以電商場景為背景進行技術面試。第一個問題&#xff0c;解釋一下Spring Boot的核心優勢是什么&#xff1f; 小C&#xff1a; Spring Boot就是開箱即用…

CUDA安裝,pytorch庫安裝

一、CUDA安裝 1.查看自己電腦適配的CUDA的最高版本 在命令提示符里輸入nvidia-smi表格右上角顯示的CUDA版本是該電腦適配的最高版本一般下載比該版本低一點的版本&#xff0c;因為會更穩定 由于本機沒有GPU所以會出現這個報錯&#xff0c;如果有GPU會出現如下報告&#xff1a…

力扣 第 463 場周賽

1. 按策略買賣股票的最佳時機 給你兩個整數數組 prices 和 strategy&#xff0c;其中&#xff1a; prices[i] 表示第 i 天某股票的價格。 strategy[i] 表示第 i 天的交易策略&#xff0c;其中&#xff1a; -1 表示買入一單位股票。 0 表示持有股票。 1 表示賣出一單位股票。 同…

Matplotlib 可視化大師系列(六):plt.imshow() - 繪制矩陣與圖像的強大工具

目錄Matplotlib 可視化大師系列博客總覽Matplotlib 可視化大師系列&#xff08;六&#xff09;&#xff1a;plt.imshow() - 繪制矩陣與圖像的強大工具一、 plt.imshow() 是什么&#xff1f;何時使用&#xff1f;二、 函數原型與核心參數三、 從入門到精通&#xff1a;代碼示例示…

小游戲AssetBundle加密方案解析

據游戲工委數據統計&#xff0c;2025年1-6月&#xff0c;國內小程序游戲市場實際銷售收入232.76億元&#xff0c;同比增長40.2%。其中內購產生收入153.03億元&#xff0c;占比65.7%&#xff0c;呈逐年提升趨勢。爆款頻出的小游戲&#xff0c;已經成為當下游戲行業的重要增長點。…

linux編程----網絡通信(TCP)

1.TCP特點1.面向數據流&#xff1b;2.有連接通信&#xff1b;3.安全可靠的通信方式&#xff1b;4.機制復雜&#xff0c;網絡資源開銷大&#xff1b;5.本質只能實現一對一的通信&#xff08;可使用TCP的并發方式實現一對多通信&#xff09;&#xff1b;2.TCP的三次握手與四次揮手…

HTTP請求的執行流程

HTTP請求的執行流程是一個系統化的過程&#xff0c;涉及多個網絡協議和交互步驟。以下是完整的流程分解&#xff0c;結合關鍵技術和邏輯順序&#xff1a;&#x1f310; 一、連接準備階段??URL解析與初始化??客戶端&#xff08;瀏覽器/應用&#xff09;解析目標URL&#xff…

聯想win11筆記本音頻失效,顯示差號(x)

該博客可以解答 常見問題詳情 Win10系統安裝更新后右下角聲音出現紅叉&#xff0c;電腦也沒有聲音&#xff0c; 通過設備管理器查看“系統設備”發現“音頻部分“出現黃色感嘆號&#xff0c; 更新驅動、卸載驅動與第三方工具檢測安裝后重啟都不行。 故障原因 應該是用戶曾經…

elasticsearch 7.x elasticsearch 使用scroll滾動查詢中超時問題案例

一 問題 1.1 問題描述 2025-08-21 16:57:53.646 | WARN ||||||||||||| scheduling-1 | ElasticsearchRestTemplate | Could not clear scroll: Unable to parse response body; nested exception is ElasticsearchStatusException [Unable to parse response body]; nested: …