A very simple news crawler in Python. Developed at Humboldt University of Berlin.
Goal | Requirements | Docker | News Sources
Fundusλ
-
A static news crawler. λ¨ λͺ μ€μ Python μ½λλ§μΌλ‘ μ¨λΌμΈ λ΄μ€ κΈ°μ¬λ₯Ό μμ½κ² ν¬λ‘€λ§ν μ μμ΅λλ€.
-
μ μ λͺ©νλ Fundusμ π°π· νΌλΈλ¦¬μ λ₯Ό μΆκ°νμ¬, λν μΈλ‘ μ¬ μ€ νλμΈ λ§€μΌκ²½μ μ λ¬Έμ¬μ κΈ°μ¬ λ΄μ©μ μλμΌλ‘ μ€ν¬λ©ν΄μ€λ κΈ°λ₯μ ꡬννλ κ²μ λλ€. μ΄λ₯Ό ν΅ν΄ νκ΅μ΄ λ΄μ€ λ°μ΄ν° μμ§ λ²μλ₯Ό νμ₯νκ³ , μ¬μ©μκ° λ§€μΌκ²½μ μ λ¬Έμ¬μ μ΅μ κΈ°μ¬μ λΉ λ₯΄κ² μ κ·Όν μ μλλ‘ μ§μν©λλ€.
python >=3.8
setuptools>=42.0, wheel # setuptools>=61.0 μμ μμ μ μΌλ‘ λμν¨μ νμΈ
python-dateutil>=2.8, <3,
lxml>=4.9, <6,
more-itertools>=9.1, <10,
cssselect>=1.1, <2,
feedparser>=6.0, <7,
colorama>=0.4, <1,
typing-extensions>=4.6, <5,
langdetect>=1.0, <2,
validators>=0.24, <1,
requests>=2.28, <3,
tqdm>=4.66, <5,
fastwarc>=0.14, <1,
chardet>=5.2, <6,
dill>=0.3, <1,
dict2xml>=1.7.6, <2,
xmltodict>=0.13.0, <1How to install & Run with Docker
π’ Dockerfile , μ΄λ―Έμ§, 컨ν
μ΄λ λ§λ€κΈ°
FROM ubuntu:22.04
RUN apt-get update
RUN apt-get install -y python3 python3-pip vim git
ENTRYPOINT ["/bin/bash"]
#λ컀 μ΄λ―Έμ§ λΉλ
docker build -t [μ΄λ¦:νκ·Έ] ./
#λ컀 μ΄λ―Έμ§ νμΈνκΈ°
docker images#λ컀 컨ν
μ΄λ λ§λ€κ³ μ€ν
docker run -dit [μ΄λ¦:νκ·Έ]
#λ컀 컨ν
μ΄λ νμΈνκΈ°
docker ps -a
#λ컀 컨ν
μ΄λ μ μ
docker exec -it <CONTAINER_ID> /bin/bashgit clone https://github.com/zxxxv/fundus.git
#Requirementsμ λͺ
μλ ν¨ν€μ§ μλ μ€μΉ
cd ~/fundus # νλ‘μ νΈ λ£¨νΈ(=pyproject.toml μλ μμΉ)λ‘ μ΄λ
pip install --upgrade pip setuptools wheel
pip install .
#κ°λ°μ© μΆκ° ν¨ν€μ§ μ€μΉ
pip install -e .[dev]
#μ€μΉ νμΈ
pip list | grep fundus
#κ²°κ³Όκ° μ΄λ κ² λμ€λ©΄ μ±κ³΅!
fundus 0.5.0Fundusλ₯Ό μ΄μ©νμ¬ νκ΅ κΈ°λ° publishersμμ κΈ°μ¬ 2κ° ν¬λ‘€λ§ν΄μ¨λ€.
from fundus import PublisherCollection, Crawler
# initialize the crawler for news publishers based in the KR
crawler = Crawler(PublisherCollection.kr)
# crawl 2 articles and print
for article in crawler.crawl(max_articles=2):
print(article)That's already it!
μ΄μ μ½λλ₯Ό μ€νν΄λ³΄λ©΄ μλμ κ°μ κ²°κ³Όκ° λμ¬κ²μ΄λ€:
Fundus-Article including 1 image(s):
- Title: "λ¬ νΈν΄, κ΅ν© λ μ€ 14μΈμ 첫 ν΅ν β¦ βκ·Όλ³Έ μμΈβ ν΄κ²° νμ μ¬μ°¨ κ°μ‘°"
- Text: "μ§λ 4μΌ(νμ§μκ°) λ¬μμ ν¬λ λ¦°κΆμ λ°λ₯΄λ©΄, λΈλΌλλ―Έλ₯΄ νΈν΄ λ¬μμ λν΅λ Ήκ³Ό λ μ€ 14μΈ κ΅ν©μ΄ ν΅νλ‘ λ¬μμ - μ°ν¬λΌμ΄λ μ μμ λν΄
λ
Όμνλ€. ν¬λ λ¦°κΆ μΈ‘μ μ±λͺ
μ λ°νν΄ νΈν΄ λν΅λ Ήμ΄ ν΅νμμ μ μΉμ Β·μΈκ΅μ μλ¨μΌλ‘ ννλ₯Ό μ΄λ£©νλ κ²μ κ΄μ¬μ΄ [...]"
- URL: https://www.mk.co.kr/news/world/11335094
- From: Maeil Business Newspaper (2025-06-05 10:56)
Fundus-Article including 1 image(s):
- Title: "λμ°μλλΉ, 380MWκΈ κ°μ€ν°λΉ μ±λ₯μν μ±κ³΅"
- Text: "λ°μ΄ν°μΌν° νλμ κ°μ€ν°λΉ μμ₯ κ³΅λ΅ λ°μ°¨ λμ°μλλΉλ¦¬ν°κ° μ체 κΈ°μ λ‘ κ°λ°ν μ΄λν κ°μ€ν°λΉ μ±λ₯μνμ μ±κ³΅νλ€. μΉνκ²½ λ°μ μμμ
λ°μ΄ν°μΌν° νλλ‘ μΈν΄ κ°μ€ν°λΉ μμκ° λμ΄λλ μν©μΈ λ§νΌ κ΄λ ¨ μμ₯ κ³΅λ΅ μ€λΉλ₯Ό λ§μΉ μ
μ΄λ€. λμ°μλλΉλ¦¬ν°λ [...]"
- URL: https://www.mk.co.kr/news/business/11335088
- From: Maeil Business Newspaper (2025-06-05 10:50)
μ΄ μΆλ ₯μ λ κ°μ κΈ°μ¬λ₯Ό μ±κ³΅μ μΌλ‘ ν¬λ‘€λ§νμμ μλ €μ€λλ€!
κ° κΈ°μ¬μ λν΄ μΆλ ₯λ¬Όμ λ€μμ μμΈν 보μ¬μ€λλ€:
- κΈ°μ¬μ ν¬ν¨λ μ΄λ―Έμ§ μ
- βμ λͺ©β(Title), μ¦ ν€λλΌμΈ
- βλ³Έλ¬Έβ(Text), μ¦ μ£Όμ κΈ°μ¬ λ³Έλ¬Έ ν μ€νΈ
- κΈ°μ¬κ° ν¬λ‘€λ§λ βURLβ
- λ΄μ€ μΆμ²(From)
νΉμ λ΄μ€ μΆμ²λ§ ν¬λ‘€λ§νκ³ μΆμ μλ μμ΅λλ€. λ§€μΌκ²½μ¬μ λ¬Έ(Maeil Business Newspaper)μμλ§ λ΄μ€ κΈ°μ¬λ₯Ό ν¬λ‘€λ§ν΄λ³΄κ² μ΅λλ€:
from fundus import PublisherCollection, Crawler
# initialize the crawler for λ§€μΌκ²½μ μ λ¬Έμ¬(MBN)
crawler = Crawler(PublisherCollection.kr.MBN)
# crawl 2 articles and print
for article in crawler.crawl(max_articles=2):
print(article)#컨ν
μ΄λ λ°μΌλ‘ λμ€κΈ°
exit
Ctrl + P κ·Έλ¦¬κ³ Ctrl + Q
#λ컀 μ»€λ° (컨ν
μ΄λ μ΄λ―Έμ§λ‘ μ μ₯νκΈ°)
docker commit [OPTIONS] <컨ν
μ΄λ_ID_or_μ΄λ¦> <μ_μ΄λ―Έμ§μ΄λ¦>:<νκ·Έ>
#컨ν
μ΄λ λ©μΆκΈ°
docker stop <CONTAINER_ID>
#컨ν
μ΄λ μμ
docker rm <CONTAINER_ID>
#μ΄λ―Έμ§ μμ
docker rmi <Image_ID λλ Image_Name:νκ·Έ>fundus/
βββ .github/ # GitHub μ€μ λ° μν¬νλ‘μ°
β βββ workflows/ # CI/CD μν¬νλ‘μ° μ μ (μ: ν
μ€νΈ, λ¦°νΈ)
βββ docs/ # νλ‘μ νΈ κ΄λ ¨ λ¬Έμ λ° νν 리μΌ
β βββ 1_getting_started.md # κΈ°λ³Έ μ¬μ©λ² μλ΄
β βββ how_to_add_a_publisher.md # νΌλΈλ¦¬μ
μΆκ° κ°μ΄λ
β βββ supported_publishers.md # μ§μ μ€μΈ νΌλΈλ¦¬μ
λͺ©λ‘
β βββ β¦ # κΈ°ν νν λ¦¬μΌ λ° κ³ κΈ μ£Όμ λ¬Έμ
βββ resources/ # μ μ 리μμ€
β βββ logo/ # λ‘κ³ μ΄λ―Έμ§ λ±
β βββ fundus-logo.png # νλ‘μ νΈ λ‘κ³
β βββ β¦ # μΆκ° λ‘κ³ νμΌ
βββ scripts/ # μ νΈλ¦¬ν° μ€ν¬λ¦½νΈ
β βββgenerate_table.py # μ§μ κ°λ₯ν νΌλΈλ¦¬μ
λͺ©λ‘ μ
β βββ β¦ # κΈ°ν μλν μ€ν¬λ¦½νΈ
βββ src/ # μμ€ μ½λ
β βββ fundus/ # Python ν¨ν€μ§ 루νΈ
β βββ __init__.py # ν¨ν€μ§ μ΄κΈ°ν νμΌ
β βββ logging.py # Fundus μ λ°μμ μ¬μ©νλ λ‘κ±°(logger) μ€μ λ° μμ± ν¨μ
β βββ py.typed # PEP 561 νμ
ννΈ λ§μ»€ νμΌ (νμ
κ²μ¬ λꡬ μΈμμ©)
β βββ parser/ # μ¬λ¬ νΌλΈλ¦¬μ
λ³ νμ(parser) κ΄λ ¨ λͺ¨λ
β β βββ __init__.py # νΌλΈλ¦¬μ
ν¨ν€μ§ μ΄κΈ°ν
β β βββ base_parser.py # λͺ¨λ νΌλΈλ¦¬μ
νμκ° μμνλ κ³΅ν΅ κΈ°λ₯(BaseParser) μ μ
β β βββ data.py # νμ±λ κΈ°μ¬ λ°μ΄ν° ꡬ쑰(λͺ¨λΈ) μ μ
β β βββ β¦ # μΆκ°λ λ€λ₯Έ νΌλΈλ¦¬μ
λ³ νμ λͺ¨λλ€
β βββ scraping/ # μΉ ν¬λ‘€λ§ λ° μ€ν¬λν λ‘μ§ κ΄λ ¨ λͺ¨λ
β β βββ __init__.py # scraping ν¨ν€μ§ μ΄κΈ°ν
β β βββ article.py # ν¬λ‘€λ§λ μΉ νμ΄μ§(HTML)μ λ©νμ 보λ₯Ό λ΄λ Article ν΄λμ€ μ μ
β β βββ crwaler.py # μ€μ μΉμ¬μ΄νΈλ₯Ό μννλ©° Article κ°μ²΄λ₯Ό μμ±νλ Crawler ꡬν
β β βββ β¦ # μμ², νν°, HTML νμ± λ± κΈ°ν μ€ν¬λν μ§μ λͺ¨λ
β βββ publishers/ # νΌλΈλ¦¬μ
λ³ μ€ν λ° νμ μ μ
β β βββ __init__.py # νΌλΈλ¦¬μ
ν¨ν€μ§ μ΄κΈ°ν
β β βββ us.py # λ―Έκ΅ κΈ°λ° νΌλΈλ¦¬μ
(μ: TheNewYorker λ±)
β β βββ uk.py # μκ΅ κΈ°λ° νΌλΈλ¦¬μ
(μ: TheGuardian λ±)
β β βββ kr/ # νκ΅ νΌλΈλ¦¬μ
κ΄λ ¨ λͺ¨λ
β β β βββ __init__.py # KR νΌλΈλ¦¬μ
ν¨ν€μ§ μ΄κΈ°ν
β β β βββ mbn.py # λ§€μΌκ²½μ μ λ¬Έμ¬(MBN) νμ ꡬν
β β β βββ β¦ # μΆκ°λ λ€λ₯Έ νκ΅ μΈλ‘ μ¬ νμ
β β βββ β¦ # κΈ°ν κ΅κ°λ³ νΌλΈλ¦¬μ
μ μ
β βββ utils/ # λ΄λΆ λμ°λ―Έ ν¨μ λ° κ³΅μ© μ νΈλ¦¬ν°
β βββ __init__.py # μ νΈλ¦¬ν° ν¨ν€μ§ μ΄κΈ°ν
β βββ events.py # Fundus λ΄ μ΄λ²€νΈ(μ: ν¬λ‘€λ§ μμ/μλ£)μ μμ λ° ν¬νΌ ν¨μ μ μ
β βββ iteration.py # λ°λ³΅(iteration) κ΄λ ¨ μ νΈλ¦¬ν° (μ²ν¬ λΆν , 무ν λ°λ³΅ λ±) ꡬν
β βββ β¦ # κΈ°ν κ³΅ν΅ ν¨μ λͺ¨μ
βββ tests/ # ν
μ€νΈ μ½λ
β βββ test_crawler.py # ν¬λ‘€λ¬ λμ κ²μ¦ ν
μ€νΈ
β βββ test_publishers.py # νΌλΈλ¦¬μ
νμ ν
μ€νΈ
β βββ β¦ # κΈ°ν μ λ/ν΅ν© ν
μ€νΈ
βββ .gitignore # Gitμμ 무μν νμΌ/ν΄λ λͺ©λ‘
βββ CODE_OF_CONDUCT.md # μ€νμμ€ κΈ°μ¬λ₯Ό μν νλ κ°λ Ή
βββ LICENSE # MIT λΌμ΄μ μ€
βββ MANIFEST.in # ν¨ν€μ§ μ ν¬ν¨ν νμΌ μ§μ
βββ README.md # νλ‘μ νΈ κ°μ λ° μ€μΉ/μ¬μ©λ² μ€λͺ
βββ pyproject.toml # λΉλ λ° νλ‘μ νΈ μ€μ νμΌ
μ¬κΈ°μμ νμ¬ μ§μλλ νΌλΈλ¦¬μ λ₯Ό νμΈν μ μμ΅λλ€. here.