Xcrap é um framework originalmente feito para Node.js, mas, digamos que eu também sou um desenvolvedor Python e, estava um pouco entediado; por isso, resolvi fazer uma versão para Python.
Ainda está em fase experimental, mas já conta com funcionalidades poderosas portadas da versão original em TypeScript, garantindo paridade entre os ecossistemas.
A ideia é ser declarativo e fácil. Veja um exemplo de como você já pode usar o xcrap para extrair dados estruturados:
fromxcrap.extractorimportHtmlExtractionModel, HtmlBaseField, HtmlNestedField, cssfromxcrap.clientsimportHttpxClientimportasyncio# 1. Defina seus modelos de forma declarativaclassAuthorModel(HtmlExtractionModel):
name=HtmlBaseField(query=css("small.author::text"))
link=HtmlBaseField(query=css("a::attr(href)"))
classQuoteModel(HtmlExtractionModel):
text=HtmlBaseField(query=css("span.text::text"))
# Modelos aninhados sem esforço!author=HtmlNestedField(model=AuthorModel) tags=HtmlBaseField(query=css("div.tags a.tag::text"), multiple=True)
classQuotesPageModel(HtmlExtractionModel):
quotes=HtmlNestedField(query=css("div.quote"), model=QuoteModel, multiple=True)
asyncdefmain():
client=HttpxClient()
# 2. Busque a páginaresponse=awaitclient.fetch(url="http://quotes.toscrape.com")
# 3. Transforme em um parser e extraia os dadosparser=response.as_html_parser()
data=parser.extract_model(QuotesPageModel)
forquoteindata["quotes"][:3]:
print(f"Quote: {quote['text']}")
print(f"Author: {quote['author']['name']}")
if__name__=="__main__":
asyncio.run(main())Suporte integrado para descriptografar respostas HTTP (AES-CBC/ECB).
fromxcrap.coreimportdecrypt_clientfromxcrap.utils.decryptionimportDecryptConfig, DecryptKeyConfigconfig=DecryptConfig(
algorithm="aes-256-cbc",
key=DecryptKeyConfig(value="sua-chave-aqui"),
iv=DecryptKeyConfig(value="seu-iv-aqui")
)
@decrypt_client(config)classMySecureClient(HttpxClient):
pass# Todas as respostas deste cliente agora virão descriptografadas!O sistema de Factory permite a criação dinâmica de componentes a partir de arquivos de configuração (JSON/Dict), facilitando a manutenção de bots sem alteração de código.
Constrói modelos complexos de forma recursiva:
fromxcrap.factoryimportcreate_extraction_modelfromxcrap.extractorimportHtmlExtractionModelconfig= {
"type": "html",
"model": {
"title": {"query": {"type": "css", "value": "h1::text"}},
"items": {
"query": {"type": "css", "value": "li"},
"multiple": true,
"nested": {
"type": "html",
"model": {"name": {"query": {"type": "css", "value": "span::text"}}}
}
}
}
}
model=create_extraction_model(config, allowed_models={"html": HtmlExtractionModel})Instancia clientes com configurações específicas:
fromxcrap.factoryimportcreate_clientfromxcrap.clientsimportHttpxClientclient=create_client(
client_type="httpx",
allowed_clients={"httpx": HttpxClient},
options={"user_agent": "CustomAgent", "proxy_url": "http://..."}
)Cria extratores a partir de strings, suportando argumentos:
fromxcrap.factoryimportcreate_extractor# 'attr:src' chamará o gerador 'attr' com o argumento 'src'get_src=create_extractor("attr:src", allowed_extractors={"attr": lambdaa: lambdael: el.attrib.get(a)})Você pode passar funções customizadas para processar campos individualmente:
classMyModel(HtmlExtractionModel):
# 'extractor' permite transformar o dado logo após a capturaprice=HtmlBaseField(
query=css(".price::text"), extractor=lambdatext: float(text.replace("$", ""))
)Também suportamos extração de JSON usando JMESPath:
fromxcrap.extractorimportJsonExtractionModel, JsonBaseField, jmes_path, JsonParserclassProjectModel(JsonExtractionModel):
name=JsonBaseField(query=jmes_path("title"))
tags=JsonBaseField(query=jmes_path("tags"), multiple=True)
content='{"title": "Xcrap", "tags": ["python", "scraping"]}'parser=JsonParser(content)
data=parser.extract_model(ProjectModel) # {'name': 'Xcrap', 'tags': ['python', 'scraping']}Para rodar os testes e verificar a cobertura:
poetry run pytest --cov=xcrap --cov-report=term-missingAtualmente o projeto conta com 100% de cobertura de código, garantindo a confiabilidade de todas as funcionalidades.
Feito com ❤️ por Marcuth contact@marcuth.dev