惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Security Archives - TechRepublic
Security Archives - TechRepublic
T
The Exploit Database - CXSecurity.com
P
Proofpoint News Feed
Scott Helme
Scott Helme
NISL@THU
NISL@THU
Cisco Talos Blog
Cisco Talos Blog
C
Cybersecurity and Infrastructure Security Agency CISA
AWS News Blog
AWS News Blog
V
Vulnerabilities – Threatpost
J
Java Code Geeks
U
Unit 42
The GitHub Blog
The GitHub Blog
H
Help Net Security
T
Tenable Blog
aimingoo的专栏
aimingoo的专栏
Jina AI
Jina AI
Spread Privacy
Spread Privacy
Apple Machine Learning Research
Apple Machine Learning Research
人人都是产品经理
人人都是产品经理
L
Lohrmann on Cybersecurity
T
Threatpost
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Engineering at Meta
Engineering at Meta
A
About on SuperTechFans
I
InfoQ
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog
L
LINUX DO - 最新话题
K
Kaspersky official blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threat Research - Cisco Blogs
C
Check Point Blog
T
The Blog of Author Tim Ferriss
有赞技术团队
有赞技术团队
宝玉的分享
宝玉的分享
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
A
Arctic Wolf
Y
Y Combinator Blog
N
News | PayPal Newsroom
M
MIT News - Artificial intelligence
Latest news
Latest news
H
Hacker News: Front Page
Blog — PlanetScale
Blog — PlanetScale
腾讯CDC
I
Intezer
爱范儿
爱范儿
F
Fortinet All Blogs
P
Palo Alto Networks Blog
C
CERT Recently Published Vulnerability Notes

Pierce Freeman

A browser for agents | Pierce Freeman The grey market of podcast appearances The way I travel | Pierce Freeman Fixing slow AWS uploads | Pierce Freeman Local tools should still use vaults We solved scratch content first Starting a podcast in 2025 Being late but still being early Automating our home video imports Adding my parents to tailscale A deep dive on agent sandboxes Language servers for AI | Pierce Freeman My simple home podcast studio We need centralized infrastructure | Pierce Freeman Coercing agents to follow conventions using AST validation My unified theory of social selling My personal backup strategy | Pierce Freeman July updates to the homelab How the KV Cache works httpx is the right way to do web requests in Python Reputation is becoming everything | Pierce Freeman Building a (kind of) invisible mac app Updated knowledge in language models Making an ascii animation | Pierce Freeman How speculative decoding works | Pierce Freeman Under the hood of Claude Code Doing things because they're easy, not hard Speeding up sideeffects with JIT in mountaineer Firehot for hot reloading in Python Misadventures in Python hot reloading How text diffusion works | Pierce Freeman The tenacity of modern LLMs The ergonomics of rails | Pierce Freeman How language servers work | Pierce Freeman Just add eggs | Pierce Freeman Unfortunately SEO still matters | Pierce Freeman The futility of human-only web requirements Setting up Input Leap | Pierce Freeman Checking in on Waymo | Pierce Freeman The react revolution | Pierce Freeman Speeding up many small transfers to a unifi nas Quick notes on swift libraries AI engineering is a different animal San Francisco | Pierce Freeman Debugging a mountaineer rendering segfault Local network config on macOS Building our home network | Pierce Freeman Introducing Envelope.dev | Pierce Freeman Legacy code and AI copilots Typehinting from day-zero | Pierce Freeman Generating database migrations with acyclic graphs Lofoten | Pierce Freeman Mountaineer v0.1: Webapps in Python and React Constraining LLM Outputs | Pierce Freeman Passthrough above all | Pierce Freeman Accuracy in kudos | Pierce Freeman How quick we are to adapt The curious case of LM repetition Costa Rica | Pierce Freeman Debugging chrome extensions with system-level logging Speeding up runpod | Pierce Freeman Inline footnotes with html templates Parsing Common Crawl in a day for $60 An era of rich CLI All or nothing with remote work The Next 10 Years | Pierce Freeman Adding wheels to flash-attention | Pierce Freeman LLMs as interdisciplinary agents | Pierce Freeman New Zealand | Pierce Freeman Representations in autoregressive models | Pierce Freeman Let's talk about Siri | Pierce Freeman Minimum viable public infrastructure | Pierce Freeman Reasoning vs. Memorization in LLMs Automatically migrate enums in alembic Greater sequence lengths will set us free On learning to ski | Pierce Freeman Dolomites | Pierce Freeman Using grpc with node and typescript Opportunity years | Pierce Freeman Buzzword peaks and valleys | Pierce Freeman Buenos Aires | Pierce Freeman Network routing interaction on MacOS Independent work: November recap | Pierce Freeman Debugging slow pytorch training performance The provenance of copy and paste Debugging tips for neural network training Patagonia | Pierce Freeman Santiago | Pierce Freeman My 2022 digital travel kit AWS vs GCP - GPU Availability V2 Independent work: October recap | Pierce Freeman Planning Patagonia | Pierce Freeman Relationship modeling | Pierce Freeman The power of status updates A new chapter | Pierce Freeman Give my library a coffee shop AWS vs GCP - GPU Availability V1 Switzerland | Pierce Freeman Webcrawling tradeoffs | Pierce Freeman Copenhagen | Pierce Freeman
Headfull browsers beat headless | Pierce Freeman
2022-09-07 · via Pierce Freeman

Twenty years ago a simple curl would open up the world. HTML markup was largely hand designed so id and name attributes were easily interpretable and parsable. Now most sites render dynamic content or use template defined class tags to define the styling of the page. To handle this richness of rendering, most production crawlers use headless browsers - at least for a part of the pipeline. Since you're running a chromium or webkit build, these should render sites exactly how users see them. In reality, headless browsers are sometimes quite different:

  1. The headless chromium build is a different executable - there are some codepaths that are only available in the full build or have different behavior in headless mode. Extensions are one example; Chrome supports them but headless does not.
  2. There are some Javascript APIs that are missing from the headless implementations, mostly with regard to viewport or other screen features.
  3. There are canvas elements that render differently - both because of missing fonts and underlying pixel driver differences.
  4. Headless browsers are harder to inspect and debug. By definition they are hidden, so they must must be debugged remotely via control tools. But because of the above points this still doesn't give a full 1:1 comparison to visible browsers.

Instead of working around the shortfalls of headless browsers, I've been building with headfull browsers recently and couldn't be happier.

Containerized Headfull

Headfull browsers are simple to use when running them locally on your desktop. In Puppeteer or Playwright, you can activate one through a headless:false parameter and use the same control code as your normal headless logic. Chromium will launch a window that behaves almost 1:1 like Chrome.

When running in the cloud within a container orchestration framework like Docker, though, headfull browsers requires more work and some finesse. The standard ubuntu-base image is intended for CLI execution so doesn't come with any of the requisite display code needed for headfull mode.

If you're interested in a pre-packaged docker solution, skip to the bottom.

Base Image

One thought for building a headfull container is whether we can build on top of an existing base. After all, Playwright ships with an image that makes it easy to get started in docker headless mode. When running in headfull mode it throws the following error:

> Looks like you launched a headed browser without having a XServer running.

> [pid=106][err] [106:106:0907/081624.714406:ERROR:ozone_platform_x11.cc(247)] Missing X server or $DISPLAY
> [pid=106][err] [106:106:0907/081624.714534:ERROR:env.cc(226)] The platform failed to initialize.  Exiting.

Installing an XServer gets past this error but throws others in turn.

In theory we could leverage this base and install the additional libraries iteratively. However one drawback of their base image is that it bundles all browsers together: chromium, webkit, and firefox. If you're only using one browser in your deployment, these additional browsers are just killing bandwidth. A larger container payload means longer bootup times in a serverless function or cluster deployment when containers have to be downloaded. Instead, you'll likely want to fork it and only install the bare minimum.

Font / Library Support

Headfull Chromium needs to access core libraries that the headless version doesn't need. These fulfill some of the system logic that I was referring to above, mostly around cursors, rendering functions, and font support. apt-get carries all of these as packages so installation is longwinded but straightforward.

$ apt-get install conf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
    libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
    libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libxcb1 libxcomposite1 \
    libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
    ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget \
    xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic libgbm-dev

This clears up the Chrome dependency issues that will prevent launch. Now we have to render the browser graphics somewhere.

X11 Support

Chromium needs to be launched in an environment where the graphics code can be successfully painted. After all, that's the key differentiating factor of headfull browsers. Containers don't have the notion of a window server, however, since they're just a raw shell implementation. We therefore virtualize a display through X11, which mirrors the X sever that some OS implementations are built off of.

$ apt-get install xvfb x11-apps x11-xkb-utils libx11-6 libx11-xcb1

The xvfb-run script that's bundled into xvfb makes it easy to launch a virtual display and spawn an executable that will draw into this display.

$ xvfb-run --server-args="-screen 0 1524x768x24" npm run start

VNC Support

While debugging it's often helpful to view the browser's current state, inspecting the viewport and DOM. To connect with the X11 server from your host and therefore bridge from host->container, we'll need a VNC server to be hosted inside the container. This is the most common screen sharing protocol and plays nicely with X11.

apt-get install x11vnc

The server can then be launched on the given screen, in this case display :0. This can then be accessed over 5900 when you port forward to your host.

x11vnc -display :0 -noxrecord -noxfixes -noxdamage -quiet -forever -passwd mypassword

All Together

There's a bit of nuance to stringing these dependencies together into an entrypoint. xvfb-run won't work out of the box because it launches the display, runs the application, and cleans up before yielding. This gives no time for us to launch our VNC server. It also intercepts some SIGINT and SIGTERM signals that we would rather forward to our application code.

For a pre-packaged Docker image that contains all the above, plus execution code and a more detailed getting started guide, see my headfull-chromium image. It's licensed under Apache and free for personal and commercial use.