@danhachuel/thunderbolt 0.6.60 → 0.6.61
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.
|
@@ -185,23 +185,45 @@ def _metric_text_value(value: Any) -> int | None:
|
|
|
185
185
|
return int(digits) * multiplier
|
|
186
186
|
|
|
187
187
|
|
|
188
|
+
def _extract_bio_from_html(document: str) -> str:
|
|
189
|
+
"""Extract only the JSON biography value; never use an SEO description as Bio."""
|
|
190
|
+
patterns = (
|
|
191
|
+
r'"biography"\s*:\s*"((?:\\.|[^"\\])*)"',
|
|
192
|
+
r"'biography'\s*:\s*'((?:\\.|[^'\\])*)'",
|
|
193
|
+
)
|
|
194
|
+
for pattern in patterns:
|
|
195
|
+
for match in re.finditer(pattern, document, flags=re.IGNORECASE | re.DOTALL):
|
|
196
|
+
raw_value = match.group(1)
|
|
197
|
+
try:
|
|
198
|
+
decoded = json.loads(f'"{raw_value}"')
|
|
199
|
+
except (TypeError, ValueError, json.JSONDecodeError):
|
|
200
|
+
decoded = bytes(raw_value, 'utf-8').decode('unicode_escape')
|
|
201
|
+
if str(decoded or '').strip():
|
|
202
|
+
return str(decoded).strip()
|
|
203
|
+
return ''
|
|
204
|
+
|
|
205
|
+
|
|
188
206
|
def _embedded_profile_user(document: str, username: str) -> Mapping[str, Any]:
|
|
189
|
-
wanted = username.casefold()
|
|
207
|
+
wanted = str(username or '').strip().lstrip('@').casefold()
|
|
190
208
|
for payload in _embedded_json_documents(document):
|
|
191
209
|
for node in _walk_json(payload):
|
|
192
210
|
if not isinstance(node, Mapping):
|
|
193
211
|
continue
|
|
194
|
-
node_username = str(node.get('username') or node.get('handle') or '').lstrip('@').casefold()
|
|
195
|
-
|
|
212
|
+
node_username = str(node.get('username') or node.get('handle') or '').strip().lstrip('@').casefold()
|
|
213
|
+
has_profile_fields = any(key in node for key in ('biography', 'bio', 'edge_followed_by', 'edge_follow', 'followers', 'following'))
|
|
214
|
+
if node_username == wanted and has_profile_fields:
|
|
196
215
|
return node
|
|
197
216
|
return {}
|
|
198
217
|
|
|
199
218
|
|
|
200
219
|
def _extract_profile_user_from_html(document: str, username: str) -> dict[str, Any]:
|
|
201
220
|
embedded_user = _embedded_profile_user(document, username)
|
|
221
|
+
regex_biography = _extract_bio_from_html(document)
|
|
202
222
|
seo_description = _meta(document, 'og:description')
|
|
203
|
-
biography = str(embedded_user.get('biography') or embedded_user.get('bio') or '').strip()
|
|
204
|
-
|
|
223
|
+
biography = str(embedded_user.get('biography') or embedded_user.get('bio') or regex_biography or '').strip()
|
|
224
|
+
print(f'embedded_user for @{username}: {embedded_user}')
|
|
225
|
+
print(f'biography extracted for @{username}: {biography!r}')
|
|
226
|
+
description = biography
|
|
205
227
|
title = str(embedded_user.get('full_name') or embedded_user.get('name') or _meta(document, 'og:title') or username).split('(')[0].strip() or username
|
|
206
228
|
avatar_url = str(embedded_user.get('profile_pic_url_hd') or embedded_user.get('profile_pic_url') or _meta(document, 'og:image')).strip()
|
|
207
229
|
|
|
@@ -235,7 +257,7 @@ def _extract_profile_user_from_html(document: str, username: str) -> dict[str, A
|
|
|
235
257
|
'profile_pic_url': avatar_url,
|
|
236
258
|
'_html_posts': posts,
|
|
237
259
|
}
|
|
238
|
-
return user if any((
|
|
260
|
+
return user if any((biography, followers, following, post_count, country, posts)) else {}
|
|
239
261
|
|
|
240
262
|
|
|
241
263
|
def _fetch_web_profile_user(username: str) -> dict[str, Any] | None:
|
package/package.json
CHANGED