facebook-gql-scraper 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- facebook_gql_scraper-0.5.1/PKG-INFO +513 -0
- facebook_gql_scraper-0.5.1/README.md +501 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/__init__.py +38 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/exceptions.py +79 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/facebookConsolidater.py +127 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/facebookGraphql.py +1227 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/facebookNoiseRemoval.py +126 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/facebookOperationDiscovery.py +192 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/structures.py +3623 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper/unifiedFacebookParser.py +2056 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper.egg-info/PKG-INFO +513 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper.egg-info/SOURCES.txt +15 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper.egg-info/dependency_links.txt +1 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper.egg-info/requires.txt +4 -0
- facebook_gql_scraper-0.5.1/facebook_gql_scraper.egg-info/top_level.txt +1 -0
- facebook_gql_scraper-0.5.1/pyproject.toml +25 -0
- facebook_gql_scraper-0.5.1/setup.cfg +4 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: facebook-gql-scraper
|
|
3
|
+
Version: 0.5.1
|
|
4
|
+
Summary: Scrape Facebook GraphQL data without an API key — profiles, posts, comments, reactions, groups, marketplace, search_engine.
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
Requires-Dist: iso4217parse>=0.6.2
|
|
9
|
+
Requires-Dist: mini-racer>=0.14.1
|
|
10
|
+
Requires-Dist: price-parser>=0.5.1
|
|
11
|
+
Requires-Dist: requests>=2.31.0
|
|
12
|
+
|
|
13
|
+
# Facebook GraphQL Scraper
|
|
14
|
+
|
|
15
|
+
Scrape Facebook GraphQL data without an API key — profiles, posts, comments, reactions, groups, marketplace, search_engine.
|
|
16
|
+
|
|
17
|
+
Inspired by [facebook-graphql-scraper](https://github.com/FaustRen/facebook-graphql-scraper).
|
|
18
|
+
> I couldn't find a free Facebook scraper that was still working, so I decided to build one :)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
The scraper is designed to be fast, lightweight, and reliable.
|
|
23
|
+
|
|
24
|
+
- **GraphQL-based:** Fetches data directly through Facebook's internal GraphQL endpoints.
|
|
25
|
+
- **No HTML parsing or CSS selectors:** Avoids brittle UI-dependent scraping that can break when Facebook changes its layout.
|
|
26
|
+
- **No headless browser or browser automation:** Reduces resource usage by avoiding the overhead of running a full browser.
|
|
27
|
+
|
|
28
|
+
When Facebook updates its GraphQL operation Doc IDs, the scraper automatically rediscovers them using a lightweight JavaScript runtime powered by V8 [PyMiniRacer](https://github.com/bpcreech/PyMiniRacer).
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Facebook Gql Scraper
|
|
33
|
+
│
|
|
34
|
+
▼
|
|
35
|
+
┌───────────┐
|
|
36
|
+
│ Doc ID │◄──────────────┐
|
|
37
|
+
└─────┬─────┘ │
|
|
38
|
+
│ │
|
|
39
|
+
▼ │
|
|
40
|
+
GraphQL Request │
|
|
41
|
+
│ │
|
|
42
|
+
┌─────┴─────────┐ │
|
|
43
|
+
│ │ │
|
|
44
|
+
[Doc Id Valid] [Doc Id Invalid] │
|
|
45
|
+
│ │ │
|
|
46
|
+
▼ ▼ │
|
|
47
|
+
Scrape Mini JavaScript │
|
|
48
|
+
V8 │
|
|
49
|
+
│ │
|
|
50
|
+
▼ │
|
|
51
|
+
New Doc ID ────┘
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install facebook-gql-scraper
|
|
60
|
+
```
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Quick start
|
|
64
|
+
|
|
65
|
+
Export your Facebook session cookies as JSON and save them as
|
|
66
|
+
`facebook_cookies.json` in your working directory.
|
|
67
|
+
|
|
68
|
+
- Firefox: [Cookie-Editor](https://addons.mozilla.org/en-US/firefox/addon/cookie-editor/)
|
|
69
|
+
- Windows: [EditThisCookie (V3)](https://chromewebstore.google.com/detail/ojfebgpkimhlhcblbalbfjblapadhbol?utm_source=item-share-cb)
|
|
70
|
+
|
|
71
|
+
## Profile
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
import facebook_gql_scraper as fgs
|
|
75
|
+
|
|
76
|
+
ctx = fgs.FacebookContext(cookies_file="facebook_cookies.json")
|
|
77
|
+
user = fgs.Profile.from_id(ctx, id="100084254631043")
|
|
78
|
+
# Or from username: user = fgs.Profile.from_username(ctx, username="Ishowspeed")
|
|
79
|
+
print(user.name)
|
|
80
|
+
```
|
|
81
|
+
> Pass `quiet=True` to `FacebookContext` to hide logs.
|
|
82
|
+
### Profile properties
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
## Identity/ Social
|
|
86
|
+
user.id # "123"
|
|
87
|
+
user.name # "John Doe"
|
|
88
|
+
user.alternate_name # "john donut"
|
|
89
|
+
user.gender # "MALE"
|
|
90
|
+
user.url # "https://www.facebook.com/johndoe"
|
|
91
|
+
user.status_text # "La peace"
|
|
92
|
+
user.profile_picture_url # "https://scontent...jpg"
|
|
93
|
+
user.is_verified # False
|
|
94
|
+
|
|
95
|
+
user.followers_count # '123'
|
|
96
|
+
user.following_count # '6.9k'
|
|
97
|
+
user.friends_count # '4'
|
|
98
|
+
|
|
99
|
+
## About / bio
|
|
100
|
+
user.biography # "Thank God👏🏽🙏🏽"
|
|
101
|
+
user.category # "Gaming video creator"
|
|
102
|
+
user.description # "desc"
|
|
103
|
+
user.current_location # "La Peace"
|
|
104
|
+
user.hometown # "Cincinnati, Ohio, USA"
|
|
105
|
+
user.birthday # "21 January 2005"
|
|
106
|
+
|
|
107
|
+
user.relationship # {"type": "Married", "date": "12 June 2019", "desc": "with Jane Doe"}
|
|
108
|
+
|
|
109
|
+
user.family # [
|
|
110
|
+
# {"name": "Jane Doe", "type": "Wife"},
|
|
111
|
+
# {"name": "John Dad", "type": "Father"},
|
|
112
|
+
# ]
|
|
113
|
+
|
|
114
|
+
user.language # ["English", "Spanish"]
|
|
115
|
+
user.links # [{"link": "speed.store", "desc": "speed.store"}]
|
|
116
|
+
user.works # [{"company": "Minecraft", "desc": "Senior | 2020 - present"}]
|
|
117
|
+
|
|
118
|
+
user.colleges # [
|
|
119
|
+
# {
|
|
120
|
+
# "name": "Streamer University",
|
|
121
|
+
# "date": "2025 - 2026",
|
|
122
|
+
# "subject": "Content Creation 101",
|
|
123
|
+
# "activities": None,
|
|
124
|
+
# "dorm": None,
|
|
125
|
+
# "classes": None,
|
|
126
|
+
# "desc": None,
|
|
127
|
+
# },
|
|
128
|
+
# ]
|
|
129
|
+
|
|
130
|
+
user.high_schools # [{"name": "Streamer High School", "date": "2025 - 2026"}]
|
|
131
|
+
user.hobbies # [{"name": "Gaming", "desc": ""}]
|
|
132
|
+
|
|
133
|
+
user.interests # {
|
|
134
|
+
# "musics": [{"name": "Radiohead", "desc": None}],
|
|
135
|
+
# "tv_shows": [...],
|
|
136
|
+
# "movies": [...],
|
|
137
|
+
# "games": [...],
|
|
138
|
+
# "sports": [...],
|
|
139
|
+
# }
|
|
140
|
+
|
|
141
|
+
user.travels # [{"name": "Tokyo", "date": "March 2023"}]
|
|
142
|
+
|
|
143
|
+
user.contacts # [{
|
|
144
|
+
# "socials": [{"type": "YouTube", "name": "@johndoe"}],
|
|
145
|
+
# "phones": [{"type": "Mobile", "name": "+123456789"}],
|
|
146
|
+
# "emails": [{"type": "Personal", "name": "johndoe@example.com"}],
|
|
147
|
+
# "messenger": [],
|
|
148
|
+
# "whatsapp_number": [],
|
|
149
|
+
# "media_kit": [],
|
|
150
|
+
# }]
|
|
151
|
+
|
|
152
|
+
user.name_pronunciation # [{"name": "john-Dawg"}]
|
|
153
|
+
user.nicknames # [{"type": "Short", "name": "john"}]
|
|
154
|
+
|
|
155
|
+
## business
|
|
156
|
+
user.business_rating # "4.6 (100 reviews)"
|
|
157
|
+
user.business_address # "123 Main St, Ohio"
|
|
158
|
+
user.business_hours # "Mon-Fri: 6am-7pm"
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Profile methods
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
# User's posts → Post
|
|
165
|
+
for post in user.get_posts():
|
|
166
|
+
print(post.id, post.text)
|
|
167
|
+
|
|
168
|
+
# User's photos → Photo
|
|
169
|
+
for photo in user.get_photos():
|
|
170
|
+
print(photo.id, photo.image_url)
|
|
171
|
+
|
|
172
|
+
# User's videos/reels → Video
|
|
173
|
+
for video in user.get_videos():
|
|
174
|
+
print(video.id, video.url, video.play_count)
|
|
175
|
+
|
|
176
|
+
# Users following this user → Profile
|
|
177
|
+
for follower in user.get_followers():
|
|
178
|
+
print(follower.id, follower.name)
|
|
179
|
+
|
|
180
|
+
# Users this user follows → Profile
|
|
181
|
+
for followee in user.get_followees():
|
|
182
|
+
print(followee.id, followee.name)
|
|
183
|
+
|
|
184
|
+
# Friends who follow this user → Profile
|
|
185
|
+
for friend in user.get_friends_followers():
|
|
186
|
+
print(friend.id, friend.name)
|
|
187
|
+
|
|
188
|
+
# Friends this user follows → Profile
|
|
189
|
+
for friend in user.get_friends_followees():
|
|
190
|
+
print(friend.id, friend.name)
|
|
191
|
+
```
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## Post
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
post = fgs.Post.from_id(ctx, id="253053527513133")
|
|
198
|
+
# Or from PFBID: post = fgs.Post.from_pfbid(ctx, pfbid="pfbid0qHZRZohg13A1DmZpkFvHr3yuv7y1heFNnNoijtvi5onn1ctx2sjM5rRgTMSh6EGql")
|
|
199
|
+
print(post.text)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Post properties
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
## Identity
|
|
206
|
+
post.id # "253053527513133"
|
|
207
|
+
post.creation_time # 1690761224
|
|
208
|
+
post.url # "https://web.facebook.com/Ishowspeed/posts/pfbid..."
|
|
209
|
+
post.title # None
|
|
210
|
+
post.text # "Thank you for all the prayers <3"
|
|
211
|
+
|
|
212
|
+
## Engagement
|
|
213
|
+
post.reaction_count # "47619"
|
|
214
|
+
post.share_count # 2380
|
|
215
|
+
post.comments_count # 1676
|
|
216
|
+
|
|
217
|
+
post.reactors_visible # True
|
|
218
|
+
post.top_reaction_breakdown # [
|
|
219
|
+
# {"id": "1635855486666999", "name": "Haha", "count": 21197},
|
|
220
|
+
# {"id": "1678524932434102", "name": "Like", "count": 17562},
|
|
221
|
+
# {"id": "867209233497706", "name": "Love", "count": 6761},
|
|
222
|
+
# ...
|
|
223
|
+
# ]
|
|
224
|
+
|
|
225
|
+
## Media
|
|
226
|
+
post.attachments # [Photo(id="123", image_url="https://...", url="https://...")]
|
|
227
|
+
|
|
228
|
+
## Related models
|
|
229
|
+
post.owner # Profile(id="100044296486382", name="Cristiano Ronaldo", ...)
|
|
230
|
+
post.group # Group (the group the post belongs to, if any)
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
### Post methods
|
|
234
|
+
|
|
235
|
+
```python
|
|
236
|
+
# All comments on the post → Comment
|
|
237
|
+
for comment in post.get_comments():
|
|
238
|
+
print(comment.id, comment.text)
|
|
239
|
+
|
|
240
|
+
# Users who reacted to the post → Reactor
|
|
241
|
+
for reactor in post.get_reactors():
|
|
242
|
+
print(reactor.reaction_type_id, reactor.profile.name)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Comment
|
|
248
|
+
|
|
249
|
+
A `Comment` may be a top-level comment on a post, or a reply to another comment.
|
|
250
|
+
|
|
251
|
+
### Comment properties
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
comment.id # "1234567898767876"
|
|
255
|
+
comment.text # "Nice!!"
|
|
256
|
+
comment.created_time # 1790291800
|
|
257
|
+
comment.reaction_count # 12
|
|
258
|
+
comment.reply_count # 3
|
|
259
|
+
|
|
260
|
+
comment.attachments # [Photo(id="...", image_url="https://...")]
|
|
261
|
+
comment.owner # Profile(id="...", name="John Doe", ...)
|
|
262
|
+
comment.post # Post(id="1086837787662153", ...)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Comment methods
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
# Replies to the comment → Comment
|
|
269
|
+
for comment_reply in comment.get_replies():
|
|
270
|
+
print(comment_reply.id, comment_reply.text)
|
|
271
|
+
|
|
272
|
+
# Sub-replies to the reply (Facebook's max reply depth) → Comment
|
|
273
|
+
for comment_subreply in comment_reply.get_replies():
|
|
274
|
+
print(comment_subreply.id, comment_subreply.text)
|
|
275
|
+
|
|
276
|
+
# Users who reacted to the comment → Reactor
|
|
277
|
+
for reactor in comment.get_reactors():
|
|
278
|
+
print(reactor.reaction_type_id, reactor.profile.name)
|
|
279
|
+
```
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## Group
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
group = fgs.Group.from_id(ctx, id="2781022932139267")
|
|
286
|
+
# Or from groupname: group = fgs.Group.from_id(ctx, groupname="workingmemes")
|
|
287
|
+
print(group.name)
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Group properties
|
|
291
|
+
|
|
292
|
+
```python
|
|
293
|
+
## Identity
|
|
294
|
+
group.id # "2781022932139267"
|
|
295
|
+
group.name # "Working Memes"
|
|
296
|
+
group.url # "https://www.facebook.com/groups/workingmemes"
|
|
297
|
+
group.privacy # "PUBLIC"
|
|
298
|
+
group.member_count # "1.2M members"
|
|
299
|
+
group.profile_picture_url # "https://scontent...jpg"
|
|
300
|
+
group.created_time # 1456789012
|
|
301
|
+
group.description # "Memes for the working class."
|
|
302
|
+
group.privacy_info # "Public group"
|
|
303
|
+
group.discoverability_info # "Anyone can find this group"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### Group methods
|
|
307
|
+
|
|
308
|
+
```python
|
|
309
|
+
# Group's posts → Post
|
|
310
|
+
for post in group.get_posts():
|
|
311
|
+
print(post.id, post.text)
|
|
312
|
+
|
|
313
|
+
# Group's members → GroupMember
|
|
314
|
+
for member in group.get_members():
|
|
315
|
+
print(member.join_status, member.contribution_score, member.profile.name)
|
|
316
|
+
```
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
## MarketplaceListing
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
listing = fgs.MarketplaceListing.from_id(ctx, id="123456789876789")
|
|
323
|
+
print(listing.product_details_type)
|
|
324
|
+
```
|
|
325
|
+
A listing is one of two types, indicated by `product_details_type`:
|
|
326
|
+
|
|
327
|
+
- **`"FOR_SALE_ITEM"`** — regular Marketplace listing sold by a Facebook user.
|
|
328
|
+
- **`"PARTNER_LISTING"`** — partner listing (e.g. eBay).
|
|
329
|
+
|
|
330
|
+
### MarketplaceListing Properties
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
## Identity
|
|
334
|
+
listing.id # "123456789876789"
|
|
335
|
+
listing.title # "USB Microphone"
|
|
336
|
+
listing.description # "USB microphone with spit guard..."
|
|
337
|
+
listing.url # "https://web.facebook.com/marketplace/item/123456789876789/"
|
|
338
|
+
listing.post_id # "12345123451234512"
|
|
339
|
+
listing.creation_time # 1790291735 (FOR_SALE_ITEM only)
|
|
340
|
+
|
|
341
|
+
## Pricing
|
|
342
|
+
listing.currency # "USD"
|
|
343
|
+
listing.price # 25
|
|
344
|
+
listing.strikethrough_price # 30 (FOR_SALE_ITEM only)
|
|
345
|
+
|
|
346
|
+
## Location
|
|
347
|
+
listing.location_text # "Johnstown, PA"
|
|
348
|
+
listing.latitude # 40.322570800781 (FOR_SALE_ITEM only)
|
|
349
|
+
listing.longitude # -78.931274414062 (FOR_SALE_ITEM only)
|
|
350
|
+
|
|
351
|
+
## Status
|
|
352
|
+
listing.is_live # True (FOR_SALE_ITEM only)
|
|
353
|
+
listing.is_pending # False (FOR_SALE_ITEM only)
|
|
354
|
+
listing.is_sold # False (FOR_SALE_ITEM only)
|
|
355
|
+
listing.is_hidden # False (FOR_SALE_ITEM only)
|
|
356
|
+
|
|
357
|
+
## Product details
|
|
358
|
+
listing.product_details_type # "FOR_SALE_ITEM" or "PARTNER_LISTING"
|
|
359
|
+
listing.shipping_cost_text # None (PARTNER_LISTING only)
|
|
360
|
+
listing.has_shipping_cost # None (PARTNER_LISTING only)
|
|
361
|
+
listing.delivery_window_text # None (PARTNER_LISTING only)
|
|
362
|
+
listing.product_details_page_uri # None (PARTNER_LISTING only)
|
|
363
|
+
|
|
364
|
+
## Category / commerce
|
|
365
|
+
listing.category_id # "1234554321123451"
|
|
366
|
+
listing.attributes # [{"name": "Condition", "value": "used_good"}, ...]
|
|
367
|
+
listing.delivery_types # ["IN_PERSON", "PUBLIC_MEETUP"] (FOR_SALE_ITEM only)
|
|
368
|
+
listing.is_shipping_offered # False
|
|
369
|
+
listing.is_purchase_protected # False (FOR_SALE_ITEM only)
|
|
370
|
+
listing.inventory_count # None (FOR_SALE_ITEM only)
|
|
371
|
+
listing.messaging_enabled # True (FOR_SALE_ITEM only)
|
|
372
|
+
|
|
373
|
+
## Media
|
|
374
|
+
listing.listing_photos # [
|
|
375
|
+
# Photo(id="3477221012465782", image_url="https://...jpg", url=None),
|
|
376
|
+
# Photo(id="2159451611596036", image_url="https://...jpg", url=None),
|
|
377
|
+
# ]
|
|
378
|
+
|
|
379
|
+
listing.videos # [Video(id="...", thumbnail_image_url="https://...", video_url="https://...")]
|
|
380
|
+
|
|
381
|
+
## Seller (FOR_SALE_ITEM only)
|
|
382
|
+
seller = listing.seller
|
|
383
|
+
|
|
384
|
+
seller.profile.id # "123451234512345"
|
|
385
|
+
seller.profile.name # "OneEvolutionarydawn"
|
|
386
|
+
seller.profile.profile_picture_url # "https://...jpg"
|
|
387
|
+
seller.profile.joined_time # 1615703350
|
|
388
|
+
seller.is_verified # False
|
|
389
|
+
seller.seller_ratings_are_private # False
|
|
390
|
+
seller.seller_ratings_average # 5.0
|
|
391
|
+
seller.seller_ratings_count # 10
|
|
392
|
+
seller.combined_ratings_are_private # False
|
|
393
|
+
seller.combined_ratings_average # 4.9
|
|
394
|
+
seller.combined_ratings_count # 24
|
|
395
|
+
|
|
396
|
+
## PartnerSeller (PARTNER_LISTING only)
|
|
397
|
+
partner = listing.partner_seller
|
|
398
|
+
|
|
399
|
+
partner.profile.id # "1033819751641974"
|
|
400
|
+
partner.profile.name # "RICKS HOLLYWOOD"
|
|
401
|
+
partner.profile.profile_picture_url # None
|
|
402
|
+
partner.partner_brand_name # "eBay"
|
|
403
|
+
partner.seller_review_count # 12903
|
|
404
|
+
partner.seller_positive_ratings_pct # 0.994
|
|
405
|
+
partner.seller_member_since # 883251803
|
|
406
|
+
partner.number_of_sold_listings # None
|
|
407
|
+
partner.average_ship_time # None
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
## SearchEngine
|
|
412
|
+
|
|
413
|
+
Facebook search service — profiles, posts, groups, and Marketplace listings.
|
|
414
|
+
|
|
415
|
+
```python
|
|
416
|
+
engine = fgs.SearchEngine(ctx)
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Search
|
|
420
|
+
|
|
421
|
+
Search Facebook for profiles, posts, or groups.
|
|
422
|
+
|
|
423
|
+
```python
|
|
424
|
+
it = engine.search("Ishowspeed", filter_type="profile", exact_match=True)
|
|
425
|
+
for item in it:
|
|
426
|
+
print(item.id, item.name)
|
|
427
|
+
|
|
428
|
+
# filter_type: "all" | "profile" | "post" | "group"
|
|
429
|
+
# "all" → yields Profile, Post, Group
|
|
430
|
+
# "profile" → yields Profile
|
|
431
|
+
# "post" → yields Post
|
|
432
|
+
# "group" → yields Group
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
### Search marketplace
|
|
436
|
+
|
|
437
|
+
Search Facebook Marketplace listings.
|
|
438
|
+
|
|
439
|
+
```python
|
|
440
|
+
it = engine.search_marketplace(
|
|
441
|
+
query="iphone",
|
|
442
|
+
is_stock_available=True,
|
|
443
|
+
filter_location_latitude=40.7128,
|
|
444
|
+
filter_location_longitude=-74.0060,
|
|
445
|
+
filter_price_lower_bound=0,
|
|
446
|
+
filter_price_upper_bound=1000,
|
|
447
|
+
filter_radius_km=100,
|
|
448
|
+
item_condition="used_good", # new | used_like_new | used_good | used_fair
|
|
449
|
+
last_n_days=7,
|
|
450
|
+
sort_by="CREATION_TIME_DESCEND", # PRICE_DESCEND | PRICE_ASCEND | DISTANCE_ASCEND | CREATION_TIME_DESCEND
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
for listing in it: # yields MarketplaceListing
|
|
454
|
+
print(listing.id, listing.title, listing.price)
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
---
|
|
458
|
+
|
|
459
|
+
## Events (TODO)
|
|
460
|
+
|
|
461
|
+
- [ ] Add support for scraping Facebook Events
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## Pagination Iterator
|
|
466
|
+
|
|
467
|
+
Every iterator-returning method returns a `PaginatedIterator`. Each item
|
|
468
|
+
returned by the iterator is a full model object, with all of its properties
|
|
469
|
+
and methods available.
|
|
470
|
+
|
|
471
|
+
For example, `get_followers()` returns full `Profile` objects:
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
for follower in user.get_followers():
|
|
475
|
+
# Properties: follower.name, follower.gender, follower.is_verified, etc.
|
|
476
|
+
# Methods: follower.get_posts(), follower.get_photos(), etc.
|
|
477
|
+
|
|
478
|
+
```
|
|
479
|
+
### Pagination — freeze / thaw
|
|
480
|
+
|
|
481
|
+
The iterator state can be serialized and resumed later:
|
|
482
|
+
|
|
483
|
+
```python
|
|
484
|
+
it = post.get_comments()
|
|
485
|
+
|
|
486
|
+
# Process the first 10 comments
|
|
487
|
+
for i, comment in enumerate(it):
|
|
488
|
+
print(comment.text)
|
|
489
|
+
if i == 9:
|
|
490
|
+
break
|
|
491
|
+
|
|
492
|
+
# Save state at any point
|
|
493
|
+
frozen = it.freeze()
|
|
494
|
+
with open("resume.json", "w") as f:
|
|
495
|
+
json.dump(frozen._asdict(), f)
|
|
496
|
+
|
|
497
|
+
# Later, in a new process, continue where it left off
|
|
498
|
+
with open("resume.json") as f:
|
|
499
|
+
frozen = fgs.FrozenPaginatedIterator(**json.load(f))
|
|
500
|
+
|
|
501
|
+
it2 = post.get_comments()
|
|
502
|
+
it2.thaw(frozen)
|
|
503
|
+
|
|
504
|
+
for comment in it2:
|
|
505
|
+
print(comment.text)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
---
|
|
509
|
+
|
|
510
|
+
## Contributing
|
|
511
|
+
|
|
512
|
+
🤝 Contributions are welcome! Feel free to open an issue or submit a pull request.
|
|
513
|
+
|