Skip to content

binance-scraper v0.1.0: P2P, copy-trading and announcements - #1

Merged
jehrr merged 6 commits into
mainfrom
feat/v0.1.0
Sep 24, 2026
Merged

jehrr merged 6 commits into
mainfrom
feat/v0.1.0

Conversation

@jehrr

@jehrr jehrr commented Sep 24, 2026

Copy link
Copy Markdown
Collaborator

First release of binance-scraper: three modes over binance.com's own JSON endpoints, three browser engines over one shared fetch loop, and the Scraper API for the one mode it can reach.

main holds only the scaffold commit (the family core copied from bbb-scraper), which a new repo needs as its default branch before workflows register. Everything else is in this PR.

What it reads

--mode one row per per page endpoint
p2p advert 20 POST /bapi/c2c/v2/friendly/c2c/adv/search
copytrading lead portfolio 30 POST /bapi/futures/v1/friendly/future/copy-trade/home-page/query-list
announcements article 50 GET /bapi/composite/v1/public/cms/article/list/query

Every HTML page on the site is behind AWS WAF (HTTP 202 challenge to curl; the CAPTCHA to real Chromium on announcement pages). The three endpoints answer a datacentre address normally. The engines land on a GET endpoint and issue each page as a same-origin fetch().

Evidence (all 2026-09-24, datacentre VPS)

  • Live, three engines: Playwright, pyppeteer and Selenium each ran the same 7 scenarios (P2P buy and sell, copy-trading, announcements, --concurrency, a refused --pay-type, --url) with identical results. A full USDT/EUR book came back as 207 of 207 adverts.
  • Live, Scraping Browser (country-de): Playwright and pyppeteer, all modes, 8 of 8 runs back to back. Selenium refuses a credentialled endpoint with exit 2.
  • Live, AWS WAF solve: AmazonTask on a gated announcement page took 23-40 s and cost $0.00145, and the page was served. The control (no solve) cleared 0 of 5 pages. The cookie that works is existing_token on .binance.com; the voucher on the page's own host did not work.
  • Live, Scraper API: announcements only, 100 rows, $0.0005 a page. The POST modes are not implemented there, and the README says so.
  • Offline: 346 checks over real, scrubbed API responses (fixtures_generated.json, cut by make_fixtures.py, which proves each fixture parses like its original). They include an end-to-end run of the shared loop with a fake browser. 7 controlled faults each turned the suite red on the expected check.
  • Fresh copy: README followed verbatim with the venv inside the tree. The suite passed and the first README command ran.
  • Canary: its own Run and Assert steps, extracted from canary.yml, passed locally for all three modes.

Traps handled

  • The API accepts wrong values silently. An unknown copy-trading dataType gives some other ordering, pageSize above 30 is capped at 30, and an unknown payTypes entry returns an empty list. Every parameter is therefore allowlisted, and --pay-type is checked against the site's own list.
  • The P2P side is inverted in the data. A buy query returns adverts marked SELL. Rows keep both sides.
  • HTTP 400 with an empty body is a refused parameter, not a block. It is classified rejected and exits 5, with no retry.
  • Scraping Browser profiles stay profile_locked briefly. Release takes 1.6-1.9 s after a clean disconnect, so connects retry. A 401 is explained as expired credentials.

Family findings, not changed here

  • tests.yml's engine-smoke step greps for "could not be imported", which 25 of 27 sibling suites never print, so that guard cannot fail there. Fixed in this repo only. The count is a text search over local checkouts.
  • transfermarkt-scraper sets the AWS WAF voucher as the cookie on the page host, which did not clear a page here.
  • The local bbb-scraper checkout was one commit behind its main (the Scraper API waitFor fix); that file was taken from the remote.

Not done

  • The Docker image has not been built locally (no daemon access); this PR's CI builds it.
  • The canary has not been dispatched from GitHub. What binance.com answers a US runner with is still unmeasured.
  • The repo page (topics) and the org profile row wait until the repo goes public.

🤖 Generated with Claude Code

petr and others added 6 commits September 24, 2026 08:25
All three engines land on an ungated /bapi GET endpoint and fetch each page
same-origin. Live: 7 of 7 scenarios pass on Playwright, pyppeteer and
Selenium. The AWS WAF solve now sets existing_token on the registrable
domain, which cleared the CAPTCHA where the voucher on the page host did not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- smoke_test.py rewritten over real, scrubbed API responses
  (fixtures_generated.json, cut by make_fixtures.py): values pinned, the
  shared fetch loop driven end to end with a fake browser, every engine's
  _Ops checked against the operations page_flow actually uses. 344 checks;
  7 controlled faults each turn it red on the expected check.
- ci_checks.py from the newest sibling (rakuten); the 32-hex exemption
  covers only an announcement address, and three per-mode samples are
  schema-checked.
- tests.yml: engine-smoke grepped for a phrase the suite never prints, so
  it could not fail; it now greps the suite's own SKIP line.
- canary.yml: all three modes, 3 pages each, no secrets.
- .gitignore by directory KIND: a live/ dump named *.json.page3 was
  untracked and not ignored.
- README, CONTRIBUTING, SECURITY, TROUBLESHOOTING, issue templates and
  CHANGELOG for Binance. The 451 wording claims nothing unmeasured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All four Scraping Browser endpoints in sibling .env files answered 401 on
2026-09-24, and the error told the reader to wait for another run holding
the pid. The README now says the --cdp-endpoint path is not yet verified
live against Binance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Measured through a country-de Scraping Browser profile on 2026-09-24:
Playwright and pyppeteer, all three modes, 8 of 8 back-to-back runs
complete. The first pass lost 2 of 8 to HTTP 500 profile_locked: a profile
is released 1.6-1.9 s after a clean disconnect (3 of 3), so connects now
retry 3 times, 3 s apart (page_flow.CDP_*). pyppeteer never surfaces that
500, so its connect timeout drops from 30 s to 10 s (a successful connect
took 0.8-0.95 s), and the orphaned handshake's traceback is filtered.
pyppeteer now enables Captcha.setAutoSolve like Playwright; its first
attempt failed because CDPSession.send returns a Future, not a coroutine.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bb's row class

The first CI run went red on offline (3.9) and (3.12): an inline heredoc
did `from output_writer import Business`, the donor repo's class, which no
local run executes. The --help and sample steps now call ci_checks.py, the
one implementation that knows the three per-mode samples, and smoke_test.py
fails if any workflow imports a local module inline (it flags the old file).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…roxy

60 of 238 adverts, 90 of 8,917 portfolios, 150 of 2,269 announcements, all
complete. Replaces the 'not yet measured' wording in README, CONTRIBUTING
and the canary header. The runner's region was not checked, so none of them
claims a country.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jehrr
jehrr merged commit 56c7876 into main Sep 24, 2026
7 checks passed
@jehrr
jehrr deleted the feat/v0.1.0 branch September 24, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant