안녕하세요! 저는 우크라이나 출신의 Python 개발자이자 웹 스크래핑, 데이터 분석, 처리 분야의 전문 지식을 갖춘 개발자 Max입니다.
저의 웹 스크래핑 여정은 2016년 소규모 회사의 리드 생성 문제를 해결하면서 시작되었습니다. 처음에는 Import.io 및 Kimono Labs와 같은 기성 솔루션을 사용했습니다. 하지만 차단, 부정확한 데이터 추출, 성능 문제 등의 한계에 금방 부딪혔습니다. 이로 인해 Python을 배우게 되었습니다. 요청과 lxml/beautifulsoup만으로 대부분의 웹사이트에서 데이터를 추출할 수 있었던 전성기였습니다. 그리고 스레드 작업 방법을 알고 있다면 당신은 이미 존경받는 전문가였습니다 :)
커뮤니티 회원 중 한 명이 Crawlee 블로그에 기고하기 위해 이 블로그를 썼습니다. 이와 같은 블로그를 Crawlee 블로그에 기고하려면 Discord 채널로 문의해 주세요.
저는 프리랜서로서 수년에 걸쳐 제품에 대한 소규모 솔루션과 크고 복잡한 데이터 마이닝 시스템을 구축해 왔습니다.
오늘은 2024년 파이썬을 이용한 웹 스크래핑의 현실에 대해 이야기해보고 싶습니다. 제가 가끔 접하는 실수와 겪게 될 문제를 살펴보고 그에 대한 해결책을 제시해 보겠습니다.
시작해 보세요.
요청과 아름다운 수프만 받아들이고 돈을 많이 벌어보세요...
아니요, 그런 글이 아닙니다.
예, 놀랄 수도 있습니다. 하지만 저는 6년 전, 4년 전, 그리고 2024년에 고객과 개발자가 보낸 이 메시지를 보았습니다. 불과 몇 달 전에 Reddit에서 이 문제에 대한 게시물을 읽었습니다.
간단한 코드 예시를 살펴보겠습니다. 이는 확장 없이 새로 설치하면 요청, httpx 및 aiohttp에 작동합니다.
import httpx url = 'https://www.wayfair.com/' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br, zstd", "Connection": "keep-alive", } response = httpx.get(url, headers=headers) print(response.content[:10])
인쇄 결과는 다음과 유사합니다.
b'\x83\x0c\x00\x00\xc4\r\x8e4\x82\x8a'
오류가 아닙니다. 완벽하게 유효한 서버 응답입니다. 어떻게든 인코딩되어 있습니다.
답은 Accept-Encoding 헤더에 있습니다. 위의 예에서는 브라우저에서 복사했기 때문에 브라우저에서 지원하는 모든 압축 방법("gzip, deflate, br, zstd")이 나열됩니다. Wayfair 백엔드는 Brotli인 "br"을 이용한 압축을 지원하며, 이를 가장 효율적인 방법으로 사용하고 있습니다.
위에 나열된 라이브러리 중 표준 종속성 중에서 Brotli 종속성이 없는 경우 이런 일이 발생할 수 있습니다. 그러나 Brotli가 이미 설치되어 있는 경우 모두 이 형식의 압축 해제를 지원합니다.
따라서 적절한 라이브러리를 설치하면 충분합니다.
pip install Brotli
이렇게 하면 인쇄 결과를 얻을 수 있습니다.
b'<!DOCTYPE '
확장 기능을 사용하여 설치하면 aiohttp와 httpx에 대해 동일한 결과를 얻을 수 있습니다.
pip install aiohttp[speedups] pip install httpx[brotli]
그런데 brotli 종속성을 추가하는 것이 제가 Crowlee-python에 처음으로 기여한 것이었습니다. 이들은 기본 HTTP 클라이언트로 httpx를 사용합니다.
새로운 지원 데이터 압축 형식인 zstd가 얼마 전에 등장했다는 사실도 눈치채셨을 것입니다. 아직 이를 사용하는 백엔드를 본 적이 없지만 httpx는 0.28.0 이상의 버전에서 압축 해제를 지원할 것입니다. 저는 이미 프로젝트에서 서버 응답 덤프를 압축하는 데 이를 사용하고 있습니다. aiofiles를 사용한 비동기 솔루션에서 놀라운 효율성을 보여줍니다.
제가 본 이 상황에 대한 가장 일반적인 해결책은 개발자가 Accept-Encoding 헤더 사용을 중단하여 서버에서 압축되지 않은 응답을 받는 것입니다. 그게 왜 나쁜가요? Wayfair의 메인 페이지는 비압축 시 약 1MB, 압축 시 약 0.165MB를 차지합니다.
따라서 이 헤더가 없는 경우:
그런데 문제는 그보다 좀 더 깊은 것 같아요. 많은 웹 스크래핑 개발자는 자신이 사용하는 헤더의 기능을 이해하지 못합니다. 따라서 이것이 귀하에게 해당된다면 다음 프로젝트를 진행할 때 다음 사항을 읽어보세요. 당신을 놀라게 할 수도 있습니다.
네, 맞습니다. 2023년에는 ChatGPT와 같은 대규모 언어 모델뿐만 아니라 향상된 Cloudflare 보호도 제공되었습니다.
오래 웹스크래핑을 하신 분들은 "글쎄, 우리는 이미 DataDome, PerimeterX, InCapsula 등을 다뤘다"고 말할 수도 있을 것입니다.
그러나 Cloudflare는 게임의 규칙을 바꾸었습니다. 수많은 사이트에 서비스를 제공하는 세계 최대 CDN 제공업체 중 하나입니다. 따라서 진입 장벽이 상당히 낮은 많은 사이트에서 해당 서비스를 사용할 수 있습니다. 이는 스크래핑으로부터 사이트를 보호하기 위해 의도적으로 구현된 앞서 언급한 기술과 근본적으로 다릅니다.
Cloudflare는 "요청 및 beautifulsoup를 사용하여 웹 스크래핑을 수행하는 방법"에 대한 다른 과정을 읽기 시작하면 즉시 닫을 수 있는 이유입니다. 여러분이 배운 내용이 "괜찮은" 웹사이트에서는 작동하지 않을 가능성이 크기 때문입니다.
또 다른 간단한 코드 예를 살펴보겠습니다.
from httpx import Client client = Client(http2=True) url = 'https://www.g2.com/' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br, zstd", "Connection": "keep-alive", } response = client.get(url, headers=headers) print(response)
물론 응답은 403이겠죠.
curl을 사용하면 어떨까요?
curl -XGET -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' -H 'Connection: keep-alive' 'https://www.g2.com/' -s -o /dev/null -w "%{http_code}\n"
역시 403.
왜 이런 일이 발생하나요?
Cloudflare는 개발자들 사이에서 인기 있는 많은 HTTP 클라이언트의 TLS 지문을 사용하기 때문에 사이트 관리자는 이러한 지문을 기반으로 Cloudflare가 클라이언트를 얼마나 적극적으로 차단하는지 사용자 정의할 수도 있습니다.
curl의 경우 다음과 같이 해결할 수 있습니다.
curl -XGET -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0"' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8' -H 'Accept-Language: en-US,en;q=0.5' -H 'Connection: keep-alive' 'https://www.g2.com/' --tlsv1.3 -s -o /dev/null -w "%{http_code}\n"
You might expect me to write here an equally elegant solution for httpx, but no. About six months ago, you could do the "dirty trick" and change the basic httpcore parameters that it passes to h2, which are responsible for the HTTP2 handshake. But now, as I'm writing this article, that doesn't work anymore.
There are different approaches to getting around this. But let's solve it by manipulating TLS.
The bad news is that all the Python clients I know of use the ssl library to handle TLS. And it doesn't give you the ability to manipulate TLS subtly.
The good news is that the Python community is great and implements solutions that exist in other programming languages.
This Python wrapper around the Golang library provides an API similar to requests.
pip install tls-client
from tls_client import Session client = Session(client_identifier="firefox_120") url = 'https://www.g2.com/' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br, zstd", "Connection": "keep-alive", } response = client.get(url, headers=headers) print(response)
The tls_client supports TLS presets for popular browsers, the relevance of which is maintained by developers. To use this, you must pass the necessary client_identifier. However, the library also allows for subtle manual manipulation of TLS.
This wrapper around the C library patches curl and provides an API similar to requests.
pip install curl_cffi
from curl_cffi import requests url = 'https://www.g2.com/' headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/png,image/svg+xml,*/*;q=0.8", "Accept-Language": "en-US,en;q=0.5", "Accept-Encoding": "gzip, deflate, br, zstd", "Connection": "keep-alive", } response = requests.get(url, headers=headers, impersonate="chrome124") print(response)
curl_cffi also provides TLS presets for some browsers, which are specified via the impersonate parameter. It also provides options for subtle manual manipulation of TLS.
I think someone just said, "They're literally doing the same thing." That's right, and they're both still very raw.
Let's do some simple comparisons:
Feature | tls_client | curl_cffi |
---|---|---|
TLS preset | + | + |
TLS manual | + | + |
async support | - | + |
big company support | - | + |
number of contributors | - | + |
분명히 이 비교에서는 컬_cffi가 승리합니다. 그러나 활성 사용자로서 나는 때때로 어떻게 처리해야 할지 확신할 수 없는 꽤 이상한 오류가 있다는 것을 말해야 합니다. 그리고 솔직히 말해서 지금까지는 둘 다 꽤 생생합니다.
이 문제를 해결하는 다른 라이브러리를 곧 보게 될 것 같습니다.
Scrapy는 어떻습니까? 솔직히 말해서 저는 그들의 업데이트를 실제로 따라잡지는 못합니다. 그러나 Zyte가 TLS 지문 인식을 우회하기 위해 어떤 조치를 취한다는 소식은 들어본 적이 없습니다. 따라서 기본적으로 Scrapy도 차단되지만 Scrapy Spider에서curl_cffi를 사용하는 것을 막을 수 있는 것은 없습니다.
예, 때로는 헤드리스 브라우저를 사용해야 할 때도 있습니다. 솔직히 말해서 제 생각에는 꼭 필요하지 않은 경우에도 너무 자주 사용되는 것 같습니다.
머리가 없는 상황에서도 Cloudflare 직원들은 Cloudflare Turnstile이라는 괴물을 만들어 일반 웹 스크래퍼의 삶을 어렵게 만들었습니다.
다양한 도구를 테스트하려면 이 데모 페이지를 사용하세요.
라이브러리가 브라우저에서 작동하는지 빠르게 테스트하려면 일반적인 비헤드리스 모드를 확인하는 것부터 시작해야 합니다. 자동화를 사용할 필요조차 없습니다. 원하는 라이브러리를 사용하여 사이트를 열고 수동으로 작업하면 됩니다.
이를 위해 어떤 라이브러리를 확인해 볼 가치가 있나요?
차단되어 보안문자를 풀 수 없습니다.
Playwright는 브라우저 자동화를 위한 훌륭한 라이브러리입니다. 그러나 개발자들은 이를 웹 스크래핑 도구로 개발할 계획이 없다고 명시적으로 밝혔습니다.
그리고 이 문제를 효과적으로 해결하는 Python 프로젝트는 들어본 적이 없습니다.
차단되어 보안문자를 풀 수 없습니다.
이 라이브러리는 Python에서 헤드리스 브라우저로 작업하기 위한 상당히 일반적인 라이브러리이며 경우에 따라 Cloudflare Turnstile을 우회할 수 있습니다. 하지만 대상 웹사이트에서는 차단됩니다. 또한 내 프로젝트에서 Cloudflare가 unDetected_chromedriver를 차단한 경우를 두 번 이상 만났습니다.
일반적으로 unDetected_chromedriver는 특히 내부적으로 오래된 Selenium을 사용하므로 프로젝트에 좋은 라이브러리입니다.
클릭 후 보안문자를 지나갈 수 있습니다.
개발자가 어떻게 이 기능을 구현했는지는 모르겠지만 작동합니다. 주요 특징은 웹 스크래핑을 위해 특별히 개발되었다는 것입니다. 또한 작업할 수 있는 더 높은 수준의 라이브러리인 보타사우루스도 있습니다.
단점은 아직까지는 매우 원시적이며 botasaurus-driver에는 문서가 없으며 작업하기 다소 까다로운 API가 있다는 것입니다.
요약하자면, 헤드리스 브라우징을 위한 기본 라이브러리는 unDetected_chromedriver일 것입니다. 그러나 특히 어려운 경우에는 보타사우루스를 사용해야 할 수도 있습니다.
높은 수준의 프레임워크는 유연성과 제어 측면에서 대가를 치르는 경우가 많지만 비즈니스 로직에 집중할 수 있도록 하여 개발 속도를 높이고 쉽게 만들도록 설계되었습니다.
그렇다면 2024년 웹스크래핑 프레임워크는 무엇일까요?
Scrapy를 언급하지 않고 Python 웹 스크래핑 프레임워크를 논하는 것은 불가능합니다. Scrapinghub(현재 Zyte)는 2008년에 처음 출시했습니다. 16년 동안 개발 회사에서 비즈니스 솔루션을 구축하는 데 기반이 되는 오픈 소스 라이브러리로 개발되었습니다.
Scrapy의 장점에 대해 이야기하면 별도의 글을 써도 됩니다. 하지만 저는 그 두 가지를 강조하겠습니다:
그런데 단점이 뭔가요?
최근 몇 년간 Zyte는 자체 플랫폼 개발에 더욱 주력해 왔습니다. Scrapy는 대부분 수정 사항만 받습니다.
따라서 Scrapy는 웹 스크래핑으로부터 보호되지 않는 사이트에 대한 훌륭하고 입증된 솔루션입니다. 스크래핑 방지 조치를 우회하려면 프레임워크에 필요한 솔루션을 개발하고 추가해야 합니다.
botasaurus-driver를 기반으로 구축된 브라우저 자동화를 사용하는 웹 스크래핑을 위한 새로운 프레임워크입니다. 최초 커밋은 2023년 5월 9일에 이루어졌습니다.
장점부터 살펴보겠습니다.
단점은 다음과 같습니다.
브라우저 자동화를 기반으로 웹 스크래퍼를 빠르게 구축하기 위한 좋은 프레임워크입니다. 저와 같은 사용자에게는 매우 중요한 HTTP 클라이언트에 대한 유연성과 지원이 부족합니다.
Python 생태계의 웹 스크래핑을 위한 새로운 프레임워크입니다. 최초 커밋은 2024년 1월 10일에 이루어졌으며 2024년 7월 5일 미디어 공간에 공개되었습니다.
Crawlee는 크롤링과 스크래핑을 처음부터 끝까지 다루고 신뢰할 수 있는 스크래퍼를 구축하도록 도와줍니다. 빠릅니다.
? Python용 Crawlee는 얼리 어답터에게 열려 있습니다!
크롤러는 기본 구성을 사용해도 거의 인간과 비슷하게 보이며 최신 봇 보호의 레이더를 피해 날아갑니다. Crawlee는 기술적 세부 사항에 대해 걱정할 필요 없이 웹에서 링크를 크롤링하고, 데이터를 스크랩하고, 기계가 읽을 수 있는 형식으로 지속적으로 저장할 수 있는 도구를 제공합니다. 그리고 풍부한 구성 옵션 덕분에 기본 설정으로 충분하지 않은 경우 Crawlee의 거의 모든 측면을 프로젝트 요구 사항에 맞게 조정할 수 있습니다.
? Crawlee 프로젝트 웹사이트에서 전체 문서, 가이드 및 예제 보기 ?
또한 프로젝트에 탐색하고 활용할 수 있는 Crawlee의 TypeScript 구현도 있습니다. GitHub의 JS/TS용 Crawlee에 대한 자세한 내용을 보려면 GitHub 저장소를 방문하세요.
우리는…
Apify에서 개발한 이 앱은 2019년 7월 9일에 처음 출시된 유명한 JS 프레임워크 크롤리를 Python으로 적용한 것입니다.
이것은 시장에 출시된 완전히 새로운 솔루션이므로 현재 활발한 설계 및 개발 단계에 있습니다. 커뮤니티도 개발에 적극적으로 참여하고 있습니다. 그러면 우리는 이미 cur_cffi의 사용이 논의되고 있음을 알 수 있습니다. 자신만의 Rust 기반 클라이언트를 만드는 가능성에 대해서는 이전에 논의했습니다. 회사가 그 생각을 버리지 않았으면 좋겠습니다.
Crawlee 팀의 메시지:
"예, 우리는 앞으로도 Python용 Crawlee를 계속해서 개선할 것입니다."
저는 개인적으로 대기업에서 Python용 HTTP 클라이언트를 개발하고 유지관리하는 모습을 보고 싶습니다. 그리고 Rust는 Python용 라이브러리 언어로서 그 자체를 매우 잘 보여줍니다. 최소한 Ruff와 Pydantic v2는 기억하자.
장점:
프레임워크는 웹 스크래핑 시장에서 이 분야에 대한 전문 지식을 갖춘 확고한 회사에 의해 개발되었습니다.
별도로 말하자면 꽤 좋은 모듈식 아키텍처를 갖추고 있습니다. 개발자가 여러 HTTP 클라이언트 간 전환 기능을 도입하면 개발 팀의 간단한 구현을 통해 사용되는 기술을 쉽게 변경할 수 있는 다소 유연한 프레임워크를 갖게 될 것입니다.
결점:
cralee-python의 성공 여부는 주로 커뮤니티에 달려 있다고 생각합니다. 튜토리얼 수가 적기 때문에 초보자에게는 적합하지 않습니다. 그러나 숙련된 개발자라면 Scrapy 대신 사용해 보기로 결정할 수도 있습니다.
장기적으로 보면 Scrapy와 Botasaurus보다 더 나은 솔루션이 될 수도 있습니다. 이는 이미 HTTP 클라이언트 작업, 브라우저 자동화, 브라우저 간 신속한 전환을 위한 유연한 도구를 제공합니다. 그러나 스크래핑 보호를 우회하는 도구가 부족하며 향후 해당 구현이 프레임워크를 선택하는 데 결정적인 요소가 될 수 있습니다.
여기까지 읽으셨다면 흥미롭고 도움이 되셨으리라 생각합니다. :)
업계는 변화하고 있으며 새로운 도전을 제시하고 있습니다. 웹 스크래핑에 전문적으로 참여하고 있다면 상황을 주의 깊게 관찰해야 합니다. 다른 분야에서는 구식 기술을 사용하여 제품을 만드는 개발자로 남을 것입니다. 하지만 현대 웹 스크래핑에서는 전혀 작동하지 않는 웹 스크래퍼를 만드는 개발자가 됩니다.
또한 귀하는 더 큰 Python 커뮤니티의 일원이며 귀하의 지식은 우리 모두를 위한 도구를 개발하는 데 유용할 수 있다는 점을 잊지 마십시오. 보시다시피, 필요한 도구 중 상당수가 현재 문자 그대로 구축되고 있습니다.
여러분의 의견을 기꺼이 읽어보겠습니다. 또한 웹 스크래핑 전문가가 필요하거나 기사에 대해 토론하고 싶다면 Github, Linkedin, Apify, Upwork, Contra 등의 플랫폼에서 저를 찾으실 수 있습니다.
많은 관심 부탁드립니다 :)
위 내용은 Python에서 웹 스크래핑의 현재 문제와 실수, 그리고 이를 해결하는 요령!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!