newspaper3k is a news, full-text, and article metadata extraction in Python 3. Advanced docs:
newspaper3k is a news, full-text, and article metadata extraction in Python 3. Advanced docs:
.. image:: https://badge.fury.io/py/newspaper3k.svg :target: http://badge.fury.io/py/newspaper3k.svg :alt: Latest version
.. image:: https://travis-ci.org/codelucas/newspaper.svg :target: http://travis-ci.org/codelucas/newspaper/ :alt: Build status
.. image:: https://coveralls.io/repos/github/codelucas/newspaper/badge.svg?branch=master :target: https://coveralls.io/github/codelucas/newspaper :alt: Coverage status
Inspired by requests_ for its simplicity and powered by lxml_ for its speed:
"Newspaper is an amazing python library for extracting & curating articles."
-- `tweeted by`_ Kenneth Reitz, Author of `requests`_
"Newspaper delivers Instapaper style article extraction." -- `The Changelog`_
.. _tweeted by: https://twitter.com/kennethreitz/status/419520678862548992
.. _The Changelog: http://thechangelog.com/newspaper-delivers-instapaper-style-article-extraction/
Newspaper is a Python3 library! Or, view our deprecated and buggy Python2 branch_
.. _Python2 branch: https://github.com/codelucas/newspaper/tree/python-2-head
.. code-block:: pycon
>>> from newspaper import Article
>>> url = 'http://fox13now.com/2013/12/30/new-year-new-laws-obamacare-pot-guns-and-drones/'
>>> article = Article(url)
.. code-block:: pycon
>>> article.download()
>>> article.html
'<!DOCTYPE HTML><html itemscope itemtype="http://...'
.. code-block:: pycon
>>> article.parse()
>>> article.authors
['Leigh Ann Caldwell', 'John Honway']
>>> article.publish_date
datetime.datetime(2013, 12, 30, 0, 0)
>>> article.text
'Washington (CNN) -- Not everyone subscribes to a New Year's resolution...'
>>> article.top_image
'http://someCDN.com/blah/blah/blah/file.png'
>>> article.movies
['http://youtube.com/path/to/link.com', ...]
.. code-block:: pycon
>>> article.nlp()
>>> article.keywords
['New Years', 'resolution', ...]
>>> article.summary
'The study shows that 93% of people ...'
.. code-block:: pycon
>>> import newspaper
>>> cnn_paper = newspaper.build('http://cnn.com')
>>> for article in cnn_paper.articles:
>>> print(article.url)
http://www.cnn.com/2013/11/27/justice/tucson-arizona-captive-girls/
http://www.cnn.com/2013/12/11/us/texas-teen-dwi-wreck/index.html
...
>>> for category in cnn_paper.category_urls():
>>> print(category)
http://lifestyle.cnn.com
http://cnn.com/world
http://tech.cnn.com
...
>>> cnn_article = cnn_paper.articles[0]
>>> cnn_article.download()
>>> cnn_article.parse()
>>> cnn_article.nlp()
...
.. code-block:: pycon
>>> from newspaper import fulltext
>>> html = requests.get(...).text
>>> text = fulltext(html)
Newspaper can extract and detect languages seamlessly. If no language is specified, Newspaper will attempt to auto detect a language.
.. code-block:: pycon
>>> from newspaper import Article
>>> url = 'http://www.bbc.co.uk/zhongwen/simp/chinese_news/2012/12/121210_hongkong_politics.shtml'
>>> a = Article(url, language='zh') # Chinese
>>> a.download()
>>> a.parse()
>>> print(a.text[:150])
香港行政长官梁振英在各方压力下就其大宅的违章建
筑(僭建)问题到立法会接受质询,并向香港民众道歉。
梁振英在星期二(12月10日)的答问大会开始之际
在其演说中道歉,但强调他在违章建筑问题上没有隐瞒的
意图和动机。 一些亲北京阵营议员欢迎梁振英道歉,
且认为应能获得香港民众接受,但这些议员也质问梁振英有
>>> print(a.title)
港特首梁振英就住宅违建事件道歉
If you are certain that an entire news source is in one language, go ahead and use the same api :)
.. code-block:: pycon
>>> import newspaper
>>> sina_paper = newspaper.build('http://www.sina.com.cn/', language='zh')
>>> for category in sina_paper.category_urls():
>>> print(category)
http://health.sina.com.cn
http://eladies.sina.com.cn
http://english.sina.com
...
>>> article = sina_paper.articles[0]
>>> article.download()
>>> article.parse()
>>> print(article.text)
新浪武汉汽车综合 随着汽车市场的日趋成熟,
传统的“集全家之力抱得爱车归”的全额购车模式已然过时,
另一种轻松的新兴 车模式――金融购车正逐步成为时下消费者购
买爱车最为时尚的消费理念,他们认为,这种新颖的购车
模式既能在短期内
...
>>> print(article.title)
两年双免0手续0利率 科鲁兹掀背金融轻松购_武汉车市_武汉汽
车网_新浪汽车_新浪网
newspaper.build() is perfect when you know which sites to crawl. But the other question I get constantly is: "I want every article about X, across all publications — where do I get the URLs?" The answer is to search Google News for your keyword first, then feed the result links straight into newspaper for extraction.
The easiest way to query Google News programmatically is the Google News API_ from SerpApi - Search API_ (they also cover Google Search, Google Maps, and more). The two libraries snap together in a few lines:
.. code-block:: python
# pip3 install google-search-results
from serpapi import GoogleSearch
from newspaper import Article
search = GoogleSearch({
"engine": "google_news",
"q": "electric vehicles",
"api_key": "YOUR_SERPAPI_KEY", # free plan at serpapi.com
})
for result in search.get_dict()["news_results"]:
article = Article(result["link"])
article.download()
article.parse()
article.nlp()
print(article.title, "--", article.summary[:120])
This pattern of SerpApi for discovery, newspaper3k for extraction, is how most production news-monitoring pipelines are built, and it sidesteps writing a crawler for every source you care about.
.. _SerpApi - Search API: https://serpapi.com?utm_source=newspaper3k_github
.. _Google News API: https://serpapi.com/google-news-api?utm_source=newspaper3k_github
download() returns a wall instead of an articleA fair number of publishers now render their body copy client-side or sit behind an anti-bot check, so article.download() comes back holding a challenge page and article.text ends up empty. Two things fix the large majority of those cases: make the request from a residential IP, and let something else execute the page's JavaScript before newspaper parses it.
Novada_ covers both, and neither changes how you use the library. Their residential pool is just the normal config.proxies route:
.. code-block:: python
from newspaper import Article, Config
config = Config()
config.proxies = {
'http': 'http://USERNAME-zone-res:[email protected]:7777',
'https': 'http://USERNAME-zone-res:[email protected]:7777',
}
article = Article('https://example.com/some-news-story', config=config)
article.download()
article.parse()
For the JS-heavy or aggressively protected sources, their Web Unblocker hands back rendered HTML, which you pass to download(input_html=...). Newspaper never makes the request itself, so everything downstream — parsing, nlp(), images, dates — is unchanged:
.. code-block:: python
import requests
from newspaper import Article
url = 'https://example.com/some-news-story'
html = requests.post(
'https://webunlocker.novada.com/request',
headers={'Authorization': 'Bearer YOUR_NOVADA_KEY'},
data={'target_url': url, 'response_format': 'html', 'js_render': 'True'},
).text
article = Article(url)
article.download(input_html=html)
article.parse()
print(article.title, article.publish_date, len(article.text))
If all you want is the body text, newspaper.fulltext(html) takes the same HTML. Novada's pool is 100M+ residential IPs across 195+ countries, and the $15 free trial spans every product, so it costs nothing to find out whether a source that keeps failing on you is genuinely unreachable or just picky about who's asking.
.. _Novada: https://www.novada.com/?github-newspaper
Once you move past scraping a handful of articles, you'll hit the same wall every news scraper hits: 403s, captchas, rate limits, and silent shadow bans. Your code is fine — your IP is the problem. The fix is rotating residential proxies.
I personally route my own newspaper3k pipelines through Swiftproxy_ — 80M+ residential IPs across 195+ countries, a 99.89% success rate, non-expiring traffic, and a free trial so you can pressure-test it before paying. Plugging it into newspaper3k takes about four lines:
.. code-block:: python
from newspaper import Article, Config
config = Config()
config.proxies = {
'http': 'http://USERNAME:[email protected]:7777',
'https': 'http://USERNAME:[email protected]:7777',
}
# a real browser UA helps too
config.browser_user_agent = (
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) '
'AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/124.0.0.0 Safari/537.36'
)
config.request_timeout = 20
article = Article('https://example.com/some-news-story', config=config)
article.download()
article.parse()
print(article.title)
The same config object works with newspaper.build() — every article fetched by the source will rotate through residential IPs automatically:
.. code-block:: python
import newspaper
paper = newspaper.build('http://cnn.com', config=config, memoize_articles=False)
for article in paper.articles:
article.download()
article.parse()
Grab credentials and a free trial at swiftproxy.net <https://www.swiftproxy.net/?ref=codelucas>_. Use code PROXY90 for 10% off your first plan.
.. _Swiftproxy: https://www.swiftproxy.net/?ref=codelucas
news_pool will happily download hundreds of articles in parallel — and that is exactly the traffic pattern that gets a single IP rate-limited or banned mid-crawl. The clean fix is a rotating gateway: point config.proxies at one endpoint, and every request the thread pool makes leaves on a different residential IP.
IPcook_ is built around that, automatic rotation on every request, with 55M+ residential IPs across 185+ locations behind one gateway, 99.99% uptime, and average response times under 0.5s, so the proxy hop never becomes the slow part of your crawl:
.. code-block:: python
import newspaper
from newspaper import Config, news_pool
config = Config()
config.proxies = {
'http': 'http://USERNAME:[email protected]:33123',
'https': 'http://USERNAME:[email protected]:33123',
}
config.request_timeout = 15
papers = [
newspaper.build(source, config=config, memoize_articles=False)
for source in (
'https://www.bbc.com',
'https://www.reuters.com',
'https://techcrunch.com',
)
]
# downloads run concurrently; each request exits on a fresh IP
news_pool.set(papers, threads_per_source=2)
news_pool.join()
for paper in papers:
for article in paper.articles:
article.parse()
Swap in the credentials from your IPcook dashboard and the same config works everywhere else in the library too. Traffic is pay-as-you-go and never expires, so a crawl that runs once a week doesn't eat a monthly plan — though auto-renewing monthly plans are there if your pipeline runs hot. Start with 100MB free, and code WELCOME20 takes 20% off.
.. _IPcook: https://www.ipcook.com/?ref=HZLQIO&utm_source=github&utm_medium=referral&utm_campaign=codelucas_newspaper
Check out The Docs_ for full and detailed guides using newspaper.
Interested in adding a new language for us? R