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 は、信頼できないソースからのスクリプトを貼り付けないように求める警告メッセージを「コンソール」に表示します。それは本当です!このスクリプトを信頼できない場合は、ここで停止してください。ソース コードを読んで、このスクリプトの動作を理解してください。
「People」タブをクリックし、スクロールして新しいメンバーをロードします
グループ ページで、「People」に移動し、ページの一番下までスクロールします。スクロールに応じてボタン テキスト内のカウンターが増加すれば、機能していることになります。
メンバーをCSV形式でエクスポート
完了したら、または「チェックポイントのエクスポート」を実行するには、「X メンバーをダウンロード」ボタンをクリックします。ダウンロード ウィンドウが表示され、CSV ファイルの保存場所を尋ねられます。
CSV ファイルを編集して表示する
CSV ファイルをロードして表示するには、Datablist.com または任意のスプレッドシート ツールを使用します。
Facebook のリードを管理し、LinkedIn プロフィールでリードを強化します
Facebook メンバーのプロフィールを使用してリード データベースを構築します。リードをフィルタリングしてセグメント化し、連絡するのに最も関連性の高いリードを見つけます。次に、Facebook メンバーに LinkedIn プロフィールと電子メール アドレスを追加します。このステップバイステップのチュートリアルに従って、Facebook メンバーをスクレイピングし、LinkedIn プロフィールを見つけます。
Facebook グループ メンバーのメール アドレスを見つける
明確にしておきますが、 Facebook から電子メール アドレスを直接取得する方法はありません。ただし、名前、会社名、または LinkedIn プロフィール URL を使用して電子メール アドレスを見つけることができます。 Facebook グループ メンバーをスクレイピングし、メール アドレスを見つけるためのガイドをお読みください。
yarn install
yarn build
生成されたスクリプトはdist/main.min.js
にあります。