用於抓取 Facebook 群組成員並將其匯出到 CSV 檔案的腳本。此 Facebook 群組成員提取器在瀏覽器中運作,無需安裝擴充功能或使用代理程式。只需將腳本複製並貼上到 Chrome 控制台中即可。
閱讀我們的逐步指南,提取 Facebook 群組成員並尋找他們的 LinkedIn 個人資料
dist/main.min.js
var G = Object . defineProperty , Q = ( e , t , n ) => t in e ? G ( e , t , { enumerable : ! 0 , configurable : ! 0 , writable : ! 0 , value : n } ) : e [ t ] = n , M = ( e , t , n ) => ( Q ( e , "symbol" != typeof t ? t + "" : t , n ) , n ) ; function ee ( e , t ) { for ( var n = "" , r = 0 ; r < t . length ; r ++ ) n += function ( e ) { for ( var t = "" , n = 0 ; n < e . length ; n ++ ) { var r = null === e [ n ] || "u" < typeof e [ n ] ? "" : e [ n ] . toString ( ) , r = ( r = e [ n ] instanceof Date ? e [ n ] . toLocaleString ( ) : r ) . replace ( / " / g , '""' ) ; 0 < n && ( t += "," ) , t += r = 0 <= r . search ( / ("|,|n) / g ) ? '"' + r + '"' : r } return t + ` ` } ( t [ r ] ) ; var i = new Blob ( [ n ] , { type : "text/csv;charset=utf-8;" } ) , o = document . createElement ( "a" ) ; void 0 !== o . download && ( i = URL . createObjectURL ( i ) , o . setAttribute ( "href" , i ) , o . setAttribute ( "download" , e ) , document . body . appendChild ( o ) , o . click ( ) , document . body . removeChild ( o ) ) } const _ = ( t , e ) => e . some ( e => t instanceof e ) ; let P , j ; function te ( ) { return P = P || [ IDBDatabase , IDBObjectStore , IDBIndex , IDBCursor , IDBTransaction ] } function ne ( ) { return j = j || [ IDBCursor . prototype . advance , IDBCursor . prototype . continue , IDBCursor . prototype . continuePrimaryKey ] } const D = new WeakMap , E = new WeakMap , b = new WeakMap ; function re ( o ) { var e = new Promise ( ( e , t ) => { const n = ( ) => { o . removeEventListener ( "success" , r ) , o . removeEventListener ( "error" , i ) } , r = ( ) => { e ( h ( o . result ) ) , n ( ) } , i = ( ) => { t ( o . error ) , n ( ) } ; o . addEventListener ( "success" , r ) , o . addEventListener ( "error" , i ) } ) ; return b . set ( e , o ) , e } function ie ( o ) { var e ; D . has ( o ) || ( e = new Promise ( ( e , t ) => { const n = ( ) => { o . removeEventListener ( "complete" , r ) , o . removeEventListener ( "error" , i ) , o . removeEventListener ( "abort" , i ) } , r = ( ) => { e ( ) , n ( ) } , i = ( ) => { t ( o . error || new DOMException ( "AbortError" , "AbortError" ) ) , n ( ) } ; o . addEventListener ( "complete" , r ) , o . addEventListener ( "error" , i ) , o . addEventListener ( "abort" , i ) } ) , D . set ( o , e ) ) } let C = { get ( e , t , n ) { if ( e instanceof IDBTransaction ) { if ( "done" === t ) return D . get ( e ) ; if ( "store" === t ) return n . objectStoreNames [ 1 ] ? void 0 : n . objectStore ( n . objectStoreNames [ 0 ] ) } return h ( e [ t ] ) } , set ( e , t , n ) { return e [ t ] = n , ! 0 } , has ( e , t ) { return e instanceof IDBTransaction && ( "done" === t || "store" === t ) || t in e } } ; function K ( e ) { C = e ( C ) } function oe ( t ) { return ne ( ) . includes ( t ) ? function ( ... e ) { return t . apply ( S ( this ) , e ) , h ( this . request ) } : function ( ... e ) { return h ( t . apply ( S ( this ) , e ) ) } } function se ( e ) { return "function" == typeof e ? oe ( e ) : ( e instanceof IDBTransaction && ie ( e ) , _ ( e , te ( ) ) ? new Proxy ( e , C ) : e ) } function h ( e ) { if ( e instanceof IDBRequest ) return re ( e ) ; if ( E . has ( e ) ) return E . get ( e ) ; var t = se ( e ) ; return t !== e && ( E . set ( e , t ) , b . set ( t , e ) ) , t } const S = e => b . get ( e ) ; function ae ( e , t , { blocked : n , upgrade : r , blocking : i , terminated : o } = { } ) { const s = indexedDB . open ( e , t ) , a = h ( s ) ; return r && s . addEventListener ( "upgradeneeded" , e => { r ( h ( s . result ) , e . oldVersion , e . newVersion , h ( s . transaction ) , e ) } ) , n && s . addEventListener ( "blocked" , e => n ( e . oldVersion , e . newVersion , e ) ) , a . then ( e => { o && e . addEventListener ( "close" , ( ) => o ( ) ) , i && e . addEventListener ( "versionchange" , e => i ( e . oldVersion , e . newVersion , e ) ) } ) . catch ( ( ) => { } ) , a } const ce = [ "get" , "getKey" , "getAll" , "getAllKeys" , "count" ] , de = [ "put" , "add" , "delete" , "clear" ] , I = new Map ; function R ( e , t ) { if ( e instanceof IDBDatabase && ! ( t in e ) && "string" == typeof t ) { if ( I . get ( t ) ) return I . get ( t ) ; const r = t . replace ( / FromIndex$ / , "" ) , i = t !== r , o = de . includes ( r ) ; return r in ( i ? IDBIndex : IDBObjectStore ) . prototype && ( o || ce . includes ( r ) ) ? ( e = async function ( e , ... t ) { e = this . transaction ( e , o ? "readwrite" : "readonly" ) ; let n = e . store ; return i && ( n = n . index ( t . shift ( ) ) ) , ( await Promise . all ( [ n [ r ] ( ... t ) , o && e . done ] ) ) [ 0 ] } , I . set ( t , e ) , e ) : void 0 } } K ( r => ( { ... r , get : ( e , t , n ) => R ( e , t ) || r . get ( e , t , n ) , has : ( e , t ) => ! ! R ( e , t ) || r . has ( e , t ) } ) ) ; const le = [ "continue" , "continuePrimaryKey" , "advance" ] , V = { } , B = new WeakMap , U = new WeakMap , ue = { get ( e , t ) { if ( ! le . includes ( t ) ) return e [ t ] ; let n = V [ t ] ; return n = n || ( V [ t ] = function ( ... e ) { B . set ( this , U . get ( this ) [ t ] ( ... e ) ) } ) } } ; async function * fe ( ... e ) { let t = this ; if ( t = t instanceof IDBCursor ? t : await t . openCursor ( ... e ) ) { t = t ; var n = new Proxy ( t , ue ) ; for ( U . set ( n , t ) , b . set ( n , S ( t ) ) ; t ; ) yield n , t = await ( B . get ( n ) || t . continue ( ) ) , B . delete ( n ) } } function F ( e , t ) { return t === Symbol . asyncIterator && _ ( e , [ IDBIndex , IDBObjectStore , IDBCursor ] ) || "iterate" === t && _ ( e , [ IDBIndex , IDBObjectStore ] ) } K ( r => ( { ... r , get ( e , t , n ) { return F ( e , t ) ? fe : r . get ( e , t , n ) } , has ( e , t ) { return F ( e , t ) || r . has ( e , t ) } } ) ) ; var H , f = function ( e , s , a , d ) { return new ( a = a || Promise ) ( function ( n , t ) { function r ( e ) { try { o ( d . next ( e ) ) } catch ( e ) { t ( e ) } } function i ( e ) { try { o ( d . throw ( e ) ) } catch ( e ) { t ( e ) } } function o ( e ) { var t ; e . done ? n ( e . value ) : ( ( t = e . value ) instanceof a ? t : new a ( function ( e ) { e ( t ) } ) ) . then ( r , i ) } o ( ( d = d . apply ( e , s || [ ] ) ) . next ( ) ) } ) } , he = function ( e , t ) { var n = { } ; for ( i in e ) Object . prototype . hasOwnProperty . call ( e , i ) && t . indexOf ( i ) < 0 && ( n [ i ] = e [ i ] ) ; if ( null != e && "function" == typeof Object . getOwnPropertySymbols ) for ( var r = 0 , i = Object . getOwnPropertySymbols ( e ) ; r < i . length ; r ++ ) t . indexOf ( i [ r ] ) < 0 && Object . prototype . propertyIsEnumerable . call ( e , i [ r ] ) && ( n [ i [ r ] ] = e [ i [ r ] ] ) ; return n } ; class pe { constructor ( e ) { this . name = "scrape-storage" , this . persistent = ! 0 , this . data = new Map , null != e && e . name && ( this . name = e . name ) , null != e && e . persistent && ( this . persistent = e . persistent ) , this . initDB ( ) . then ( ( ) => { } ) . catch ( ( ) => { this . persistent = ! 1 } ) } get storageKey ( ) { return "storage-" + this . name } initDB ( ) { return f ( this , void 0 , void 0 , function * ( ) { this . db = yield ae ( this . storageKey , 6 , { upgrade ( e , t , n , r ) { let i ; if ( t < 5 ) try { e . deleteObjectStore ( "data" ) } catch { } ( i = e . objectStoreNames . contains ( "data" ) ? r . objectStore ( "data" ) : e . createObjectStore ( "data" , { keyPath : "_id" , autoIncrement : ! 0 } ) ) && ! i . indexNames . contains ( "_createdAt" ) && i . createIndex ( "_createdAt" , "_createdAt" ) , i && ! i . indexNames . contains ( "_groupId" ) && i . createIndex ( "_groupId" , "_groupId" ) , i && ! i . indexNames . contains ( "_pk" ) && i . createIndex ( "_pk" , "_pk" , { unique : ! 0 } ) } } ) } ) } _dbGetElem ( e , t ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) return yield ( t = t || this . db . transaction ( "data" , "readonly" ) ) . store . index ( "_pk" ) . get ( e ) ; throw new Error ( "DB doesnt exist" ) } ) } getElem ( e ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) try { return yield this . _dbGetElem ( e ) } catch ( e ) { console . error ( e ) } else this . data . get ( e ) } ) } _dbSetElem ( i , o , s = ! 1 , a , d ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) { let e = ! 1 ; const t = ( d = d || this . db . transaction ( "data" , "readwrite" ) ) . store , n = yield t . index ( "_pk" ) . get ( i ) ; if ( n ) s && ( yield t . put ( Object . assign ( Object . assign ( { } , n ) , o ) ) , e = ! 0 ) ; else { const r = Object . assign ( { _pk : i , _createdAt : new Date } , o ) ; a && ( r . _groupId = a ) , yield t . put ( r ) , e = ! 0 } return e } throw new Error ( "DB doesnt exist" ) } ) } addElem ( e , t , n = ! 1 , r ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) try { return yield this . _dbSetElem ( e , t , n , r ) } catch ( e ) { console . error ( e ) } else this . data . set ( e , t ) ; return ! 0 } ) } addElems ( t , o = ! 1 , s ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) { const n = [ ] , r = this . db . transaction ( "data" , "readwrite" ) , i = [ ] ; if ( t . forEach ( ( [ e , t ] ) => { - 1 === i . indexOf ( e ) && ( i . push ( e ) , n . push ( this . _dbSetElem ( e , t , o , s , r ) ) ) } ) , 0 < n . length ) { n . push ( r . done ) ; const e = yield Promise . all ( n ) ; let t = 0 ; return e . forEach ( e => { "boolean" == typeof e && e && ( t += 1 ) } ) , t } return 0 } return t . forEach ( ( [ e , t ] ) => { this . addElem ( e , t ) } ) , t . length } ) } deleteFromGroupId ( n ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) { let e = 0 , t = yield this . db . transaction ( "data" , "readwrite" ) . store . index ( "_groupId" ) . openCursor ( IDBKeyRange . only ( n ) ) ; for ( ; t ; ) t . delete ( ) , t = yield t . continue ( ) , e += 1 ; return e } throw new Error ( "Not Implemented Error" ) } ) } clear ( ) { return f ( this , void 0 , void 0 , function * ( ) { this . persistent && this . db ? yield this . db . clear ( "data" ) : this . data . clear ( ) } ) } getCount ( ) { return f ( this , void 0 , void 0 , function * ( ) { return this . persistent && this . db ? yield this . db . count ( "data" ) : this . data . size } ) } getAll ( ) { return f ( this , void 0 , void 0 , function * ( ) { if ( this . persistent && this . db ) { const n = new Map , e = yield this . db . getAll ( "data" ) ; return e && e . forEach ( e => { var t = e [ "_id" ] , e = he ( e , [ "_id" ] ) ; n . set ( t , e ) } ) , n } return this . data } ) } toCsvData ( ) { return f ( this , void 0 , void 0 , function * ( ) { const t = [ ] ; return t . push ( this . headers ) , ( yield this . getAll ( ) ) . forEach ( e => { try { t . push ( this . itemToRow ( e ) ) } catch ( e ) { console . error ( e ) } } ) , t } ) } } const ye = [ "display: block;" , "padding: 0px 4px;" , "cursor: pointer;" , "text-align: center;" ] ; function W ( e ) { const t = document . createElement ( "div" ) , n = [ ... ye ] ; return e && n . push ( "flex-grow: 1;" ) , t . setAttribute ( "style" , n . join ( "" ) ) , t } const ge = [ "margin-left: 4px;" , "margin-right: 4px;" , "border-left: 1px solid #2e2e2e;" ] ; function X ( ) { const e = document . createElement ( "div" ) ; return e . innerHTML = " " , e . setAttribute ( "style" , ge . join ( "" ) ) , e } function g ( e , t ) { var n , t = t || { } ; let r ; const i = document . createElement ( "span" ) ; return ( r = t . bold ? ( n = document . createElement ( "strong" ) , i . append ( n ) , n ) : i ) . textContent = e , t . idAttribute && r . setAttribute ( "id" , t . idAttribute ) , i } const me = [ "position: fixed;" , "top: 0;" , "left: 0;" , "z-index: 10000;" , "width: 100%;" , "height: 100%;" , "pointer-events: none;" ] , be = [ "position: absolute;" , "bottom: 30px;" , "right: 30px;" , "width: auto;" , "pointer-events: auto;" ] , we = [ "align-items: center;" , "appearance: none;" , "background-color: #EEE;" , "border-radius: 4px;" , "border-width: 0;" , "box-shadow: rgba(45, 35, 66, 0.4) 0 2px 4px,rgba(45, 35, 66, 0.3) 0 7px 13px -3px,#D6D6E7 0 -3px 0 inset;" , "box-sizing: border-box;" , "color: #36395A;" , "display: flex;" , "font-family: monospace;" , "height: 38px;" , "justify-content: space-between;" , "line-height: 1;" , "list-style: none;" , "overflow: hidden;" , "padding-left: 16px;" , "padding-right: 16px;" , "position: relative;" , "text-align: left;" , "text-decoration: none;" , "user-select: none;" , "white-space: nowrap;" , "font-size: 18px;" ] ; class ve { constructor ( ) { this . ctas = [ ] , this . canva = document . createElement ( "div" ) , this . canva . setAttribute ( "style" , me . join ( "" ) ) , this . inner = document . createElement ( "div" ) , this . inner . setAttribute ( "style" , be . join ( "" ) ) , this . canva . appendChild ( this . inner ) , this . history = document . createElement ( "div" ) , this . inner . appendChild ( this . history ) , this . container = document . createElement ( "div" ) , this . container . setAttribute ( "style" , we . join ( "" ) ) , this . inner . appendChild ( this . container ) } makeItDraggable ( ) { let t = 0 , n = 0 , r = 0 , i = 0 ; const o = e => { r = e . clientX - t , i = e . clientY - n , this . inner . style . right = window . innerWidth - r - this . inner . offsetWidth + "px" , this . inner . style . bottom = window . innerHeight - i - this . inner . offsetHeight + "px" } , e = ( this . inner . addEventListener ( "mousedown" , e => { e . preventDefault ( ) , t = e . clientX - this . inner . offsetLeft , n = e . clientY - this . inner . offsetTop , window . addEventListener ( "mousemove" , o , ! 1 ) } , ! 1 ) , window . addEventListener ( "mouseup" , ( ) => { window . removeEventListener ( "mousemove" , o , ! 1 ) } , ! 1 ) , document . createElement ( "div" ) ) ; e . style . cursor = "move" , e . innerHTML = '' , this . addCta ( X ( ) ) , this . addCta ( e ) } render ( ) { document . body . appendChild ( this . canva ) } addCta ( e , t ) { "u" < typeof t ? this . ctas . push ( e ) : this . ctas . splice ( t , 0 , e ) , this . container . innerHTML = "" , this . ctas . forEach ( e => { this . container . appendChild ( e ) } ) } } ! function ( e ) { e . ADD = "add" , e . LOG = "log" } ( H = H || { } ) ; class xe extends pe { constructor ( ) { super ( ... arguments ) , M ( this , "name" , "fb-scrape-storage" ) } get headers ( ) { return [ "Profile Id" , "Full Name" , "Profile Link" , "Bio" , "ImageSrc" , "GroupId" , "Group Joining Text" , "Profile Type" ] } itemToRow ( e ) { return [ e . profileId , e . fullName , e . profileLink , e . bio , e . imageSrc , e . groupId , e . groupJoiningText , e . profileType ] } } const m = new xe , J = "fb-group-scraper-number-tracker" , Ee = "groupMemberExport" ; async function A ( ) { const e = document . getElementById ( J ) ; if ( e ) { const t = await m . getCount ( ) ; e . textContent = t . toString ( ) } } const y = new ve ; function Ie ( ) { const e = W ( ) , t = ( e . appendChild ( g ( "Download " ) ) , e . appendChild ( g ( "0" , { bold : ! 0 , idAttribute : J } ) ) , e . appendChild ( g ( " users" ) ) , e . addEventListener ( "click" , async function ( ) { var e = ( new Date ) . toISOString ( ) , t = await m . toCsvData ( ) ; try { ee ( Ee + `- ${ e } .csv` , t ) } catch ( e ) { console . error ( "Error while generating export" ) , console . log ( e . stack ) } } ) , y . addCta ( e ) , y . addCta ( X ( ) ) , W ( ) ) ; t . appendChild ( g ( "Reset" ) ) , t . addEventListener ( "click" , async function ( ) { await m . clear ( ) , await A ( ) } ) , y . addCta ( t ) , y . makeItDraggable ( ) , y . render ( ) , window . setTimeout ( ( ) => { A ( ) } , 1e3 ) } function _e ( e ) { var t ; let n ; if ( null != ( t = null == e ? void 0 : e . data ) && t . group ) n = e . data . group ; else { if ( "Group" !== ( null == ( t = null == ( t = null == e ? void 0 : e . data ) ? void 0 : t . node ) ? void 0 : t . __typename ) ) return ; n = e . data . node } let r ; if ( null != ( t = null == n ? void 0 : n . new_members ) && t . edges ) r = n . new_members . edges ; else if ( null != ( e = null == n ? void 0 : n . new_forum_members ) && e . edges ) r = n . new_forum_members . edges ; else { if ( null == ( t = null == n ? void 0 : n . search_results ) || ! t . edges ) return ; r = n . search_results . edges } const i = r . map ( e => { var t = "GroupUserInvite" === e . node . __isEntity ? e . node . invitee_profile : e . node ; if ( ! t ) return null ; var { id : n , name : r , bio_text : i , url : o , profile_picture : s , __isProfile : a } = t , d = ( null == ( d = null == e ? void 0 : e . join_status_text ) ? void 0 : d . text ) || ( null == ( e = null == ( d = null == e ? void 0 : e . membership ) ? void 0 : d . join_status_text ) ? void 0 : e . text ) , t = null == ( e = t . group_membership ) ? void 0 : e . associated_group . id ; return { profileId : n , fullName : r , profileLink : o , bio : ( null == i ? void 0 : i . text ) || "" , imageSrc : ( null == s ? void 0 : s . uri ) || "" , groupId : t , groupJoiningText : d || "" , profileType : a } } ) , o = [ ] ; i . forEach ( e => { e && o . push ( [ e . profileId , e ] ) } ) , m . addElems ( o ) . then ( ( ) => { A ( ) } ) } function De ( e ) { let n = [ ] ; try { n . push ( JSON . parse ( e ) ) } catch ( t ) { var r = e . split ( ` ` ) ; if ( r . length <= 1 ) return void console . error ( "Fail to parse API response" , t ) ; for ( let e = 0 ; e < r . length ; e ++ ) { var i = r [ e ] ; try { n . push ( JSON . parse ( i ) ) } catch { console . error ( "Fail to parse API response" , t ) } } } for ( let e = 0 ; e < n . length ; e ++ ) _e ( n [ e ] ) } function Ce ( ) { Ie ( ) ; let e = XMLHttpRequest . prototype . open ; XMLHttpRequest . prototype . open = function ( ) { this . addEventListener ( "readystatechange" , function ( ) { this . responseURL . includes ( "/api/graphql/" ) && 4 === this . readyState && De ( this . responseText ) } , ! 1 ) , e . apply ( this , arguments ) } } Ce ( ) ;
開啟 Chrome 開發者控制台
若要在 Chrome 上開啟 Chrome 開發者控制台,請使用鍵盤快速鍵Ctrl + Shift + I
(在 Windows 上)或Cmd + Option + I
(在 Mac 上)。
複製貼上腳本
選擇“控制台”選項卡並複製貼上上面的腳本。 Facebook 在「控制台」中顯示警告訊息,要求不要貼上來自不可信來源的腳本。這是真的!如果您不信任此腳本,請停止。閱讀原始程式碼以了解該腳本的作用。
點擊“人員”選項卡並滾動以加載新成員
在群組頁面中,前往「人員」並捲動到頁面底部。如果按鈕文字中的計數器隨著滾動而增加,則表示它正在工作!
以 CSV 格式匯出成員
完成後,或要執行“匯出檢查點”,請按一下“下載 X 成員”按鈕。下載視窗將提示詢問 CSV 檔案的儲存位置。
編輯並檢視您的 CSV 文件
若要載入和檢視 CSV 文件,請使用 Datablist.com 或任何電子表格工具。
管理您的 Facebook 潛在客戶並透過 LinkedIn 個人資料豐富其內容
使用 Facebook 會員個人資料建立潛在客戶資料庫。篩選和細分潛在客戶以找到最相關的聯絡人潛在客戶。然後,透過 LinkedIn 個人資料和電子郵件地址豐富 Facebook 成員。請按照此逐步教學來抓取 Facebook 成員並找到他們的 LinkedIn 個人資料。
尋找 Facebook 群組成員的電子郵件地址
需要明確的是:沒有直接的方法可以從 Facebook 取得電子郵件地址。但我們可以使用姓名、公司名稱或 LinkedIn 個人資料 URL 來尋找電子郵件地址!請閱讀我們的指南來抓取 Facebook 群組成員並找到他們的電子郵件地址。
yarn install
yarn build
產生的腳本位於dist/main.min.js
中。