From 2cf32ee57ac490b44aa64796a1e3cd016ecc3131 Mon Sep 17 00:00:00 2001 From: Adrian Breiding Date: Thu, 8 Aug 2024 23:54:01 +0200 Subject: [PATCH 1/5] add cbc as first canadian publisher --- docs/supported_publishers.md | 32 ++++++++ src/fundus/publishers/__init__.py | 2 + src/fundus/publishers/ca/__init__.py | 18 +++++ src/fundus/publishers/ca/cbc_news.py | 64 +++++++++++++++ .../parser/test_data/ca/CBCNews.json | 75 ++++++++++++++++++ .../test_data/ca/CBCNews_2024_08_08.html.gz | Bin 0 -> 49787 bytes tests/resources/parser/test_data/ca/meta.info | 6 ++ 7 files changed, 197 insertions(+) create mode 100644 src/fundus/publishers/ca/__init__.py create mode 100644 src/fundus/publishers/ca/cbc_news.py create mode 100644 tests/resources/parser/test_data/ca/CBCNews.json create mode 100644 tests/resources/parser/test_data/ca/CBCNews_2024_08_08.html.gz create mode 100644 tests/resources/parser/test_data/ca/meta.info diff --git a/docs/supported_publishers.md b/docs/supported_publishers.md index 0d6d252d..ca5de0e3 100644 --- a/docs/supported_publishers.md +++ b/docs/supported_publishers.md @@ -86,6 +86,38 @@ +## CA-Publishers + + + + + + + + + + + + + + + + + + + + +
Class                                Name                                                                        URL                                                    Missing AttributesAdditional Attributes    
+ CBCNews + +
CBC News
+
+ + www.cbc.ca + +   
+ + ## CH-Publishers diff --git a/src/fundus/publishers/__init__.py b/src/fundus/publishers/__init__.py index da42fd27..cb68634e 100644 --- a/src/fundus/publishers/__init__.py +++ b/src/fundus/publishers/__init__.py @@ -4,6 +4,7 @@ from fundus.publishers.at import AT from fundus.publishers.au import AU from fundus.publishers.base_objects import Publisher, PublisherGroup +from fundus.publishers.ca import CA from fundus.publishers.ch import CH from fundus.publishers.cn import CN from fundus.publishers.de import DE @@ -61,3 +62,4 @@ class PublisherCollection(metaclass=PublisherCollectionMeta): tr = TR my = MY no = NO + ca = CA diff --git a/src/fundus/publishers/ca/__init__.py b/src/fundus/publishers/ca/__init__.py new file mode 100644 index 00000000..5013deef --- /dev/null +++ b/src/fundus/publishers/ca/__init__.py @@ -0,0 +1,18 @@ +from fundus.publishers.base_objects import Publisher, PublisherGroup +from fundus.publishers.ca.cbc_news import CBCNewsParser +from fundus.scraping.url import NewsMap, RSSFeed, Sitemap + +# noinspection PyPep8Naming + + +class CA(metaclass=PublisherGroup): + CBCNews = Publisher( + name="CBC News", + domain="https://www.cbc.ca/", + parser=CBCNewsParser, + sources=[ + RSSFeed("https://www.cbc.ca/webfeed/rss/rss-topstories"), + RSSFeed("https://www.cbc.ca/webfeed/rss/rss-world"), + RSSFeed("https://www.cbc.ca/webfeed/rss/rss-canada"), + ], + ) diff --git a/src/fundus/publishers/ca/cbc_news.py b/src/fundus/publishers/ca/cbc_news.py new file mode 100644 index 00000000..919a2c26 --- /dev/null +++ b/src/fundus/publishers/ca/cbc_news.py @@ -0,0 +1,64 @@ +import datetime +import json +import re +from typing import List, Optional + +import more_itertools +from lxml.cssselect import CSSSelector +from lxml.etree import XPath +from lxml.html import document_fromstring + +from fundus.parser import ArticleBody, BaseParser, ParserProxy, attribute +from fundus.parser.data import LinkedDataMapping +from fundus.parser.utility import ( + extract_article_body_with_selector, + generic_author_parsing, + generic_date_parsing, + generic_topic_parsing, +) + + +class CBCNewsParser(ParserProxy): + class V1(BaseParser): + _summary_selector = CSSSelector("h2.deck") + _subheadline_selector = CSSSelector("div.story > h2") + _paragraph_selector = CSSSelector("div.story > p") + + _author_ld_selector = XPath("//script[@id='initialStateDom']") + + @attribute + def body(self) -> ArticleBody: + return extract_article_body_with_selector( + self.precomputed.doc, + summary_selector=self._summary_selector, + subheadline_selector=self._subheadline_selector, + paragraph_selector=self._paragraph_selector, + ) + + @attribute + def authors(self) -> List[str]: + doc = document_fromstring(self.precomputed.html) + ld_nodes = self._author_ld_selector(doc) + try: + author_ld = json.loads(re.sub(r"(window\.__INITIAL_STATE__ = |;$)", "", ld_nodes[0].text_content())) + except json.JSONDecodeError: + return [] + if not (details := author_ld.get("detail")): + return [] + if not (content := details.get("content")): + return [] + return generic_author_parsing(content.get("authorList")) + + @attribute + def publishing_date(self) -> Optional[datetime.datetime]: + return generic_date_parsing(self.precomputed.ld.bf_search("ReportageNewsArticle")[0].get("datePublished")) + + @attribute + def title(self) -> Optional[str]: + if not (title := self.precomputed.meta.get("og:title")): + return title + return re.sub(r" \|.*", "", title) + + @attribute + def topics(self) -> List[str]: + return generic_topic_parsing(self.precomputed.ld.bf_search("ReportageNewsArticle")[0].get("articleSection")) diff --git a/tests/resources/parser/test_data/ca/CBCNews.json b/tests/resources/parser/test_data/ca/CBCNews.json new file mode 100644 index 00000000..b6632f46 --- /dev/null +++ b/tests/resources/parser/test_data/ca/CBCNews.json @@ -0,0 +1,75 @@ +{ + "V1": { + "authors": [ + "Yasmine Hassan" + ], + "body": { + "summary": [ + "The appointment came days after Ismail Haniyeh was assassinated in Tehran" + ], + "sections": [ + { + "headline": [], + "paragraphs": [ + "Hours after Yahya Sinwar was named the new leader of Hamas's political bureau on Tuesday, many in Gaza wondered how the appointment would affect the war and ceasefire negotiations with Israel.", + "The announcement, posted on Hamas's Telegram channel soon after former leader Ismail Haniyeh was killed in Iran, was seen as a defiant move from the group. Israel has characterized Sinwar as the \"mastermind\" behind the Oct. 7 attacks on southern Israel, which Israeli figures say killed 1,200 and took over 250 hostages into Gaza.", + "Sinwar, 61, has led Hamas in the Gaza Strip since 2017. But his background leans more in military rather than in politics, and his methods are seen as more extreme than his predecessor's.", + "That has created questions over how Sinwar will manage negotiations, and how Israel will negotiate with the man who they say orchestrated the attacks — and whom they've vowed to kill.", + "Many Palestinians interviewed in Gaza expressed similar concern over the promotion, although some welcomed the move. The news comes during a time of tense negotiation to end a war that has devastated the region and killed more than 39,000, according to Palestinian tallies, over the past 10 months." + ] + }, + { + "headline": [ + "Palestinians react" + ], + "paragraphs": [ + "Jamil Al Saadouni, 58, told CBC freelance videographer Mohamed El Saife in Khan Younis that Sinwar's appointment was \"an internal decision.\"", + "He lamented the fact that Palestinian civilians, who are directly impacted by the war in Gaza, were not consulted on the best replacement for Haniyeh.", + "\"This has nothing to do with other factions or the Palestinian people.\"", + "Abu Hassan Amer, 44, agreed.", + "\"Choosing a military leadership during this period can harm the negotiations,\" he told El Saife. \"Because as they say, the non-political gun creates roadblocks.\"", + "Sinwar is seen as a \"hard-liner\" even within Hamas, said Matthew Levitt, senior fellow at the Washington Institute for Near East Policy, which was founded in 1985 with support from the American Israel Public Affairs Committee, a pro-Israel lobbying organization.", + "Sinwar served over 20 years in Israeli jails in connection with the killings of two Israeli soldiers and four fellow Palestinians, and was released early in 2011 as part of a prisoner swap. He has been known to hunt down people suspected of collaborating with Israel.", + "Levitt said that because of his time in jail, Sinwar \"understands Israelis.\"", + "\"He learned Hebrew, he spoke with his jailers, and that really showed on Oct. 7, when he understood the trauma that the kidnapping and killing of a large number of people would do for the Israelis,\" he said.", + "By comparison, Haniyeh, who ruled in exile from Qatar, often took a more moderate and pragmatic stance.", + "\"The killing of Haniyeh already brought negotiations back to the drawing board,\" Lina Khatib, an expert on the conflict at U.K. think-tank Chatham House, told the AP in an interview. \"The next chess move by Hamas makes negotiations even trickier.\"", + "Haniyeh was killed by an airstrike in Tehran, where he was attending the inauguration of Iran's new president. While Hamas and Iran have blamed Israel for the strike, Israel has not claimed responsibility for it." + ] + }, + { + "headline": [ + "A military man in politics" + ], + "paragraphs": [ + "Some in Gaza welcomed the news of Sinwar's promotion, saying they needed someone to defend them.", + "\"Choosing him from the stance of Palestine is a good choice,\" Abu Anas Al Saud told El Saife. \"We need someone to defend the land that was stolen.\"", + "But Al Saud is aware of the effect Sinwar may have on ceasefire talks.", + "\"He's the most wanted man to Israel,\" he said. \"It will not advance negotiations at all.\"", + "Sinwar only made rare appearances before the war. He hasn't been seen in public since Oct. 7, and is thought to be hiding deep in tunnels beneath the Gaza Strip. Mediators say it takes several days to exchange messages with him, raising questions on how he will now manage Hamas as its international face.", + "Sinwar \"is someone who grew up within the brigade and the militant terrorist wing of Hamas,\" said Levitt.", + "However, while Sinwar's promotion might seem like a direct \"challenge to Israel,\" a deal was still possible, Sadeq Abu Amer told the AP. He noted that Sinwar \"might take a step that will surprise everyone.\" Abu Amer is the head of the Palestinian Dialogue Group in Turkey, which says on its site that it aims to \"protect the historical rights of the Palestinian people.\"", + "And while the assassination of Haniyeh makes a difference \"in the immediate,\" Levitt said, in the long term, both sides are still looking for a deal.", + "\"The same factors that were driving Hamas towards the deal and separately driving the Israeli prime minister to a deal are still there.\"" + ] + }, + { + "headline": [ + "'There is only one place for Yahya Sinwar'" + ], + "paragraphs": [ + "On Tuesday, Israel's chief military spokesperson, Rear Admiral Daniel Hagari, said Sinwar's appointment would not stop Israel from pursuing him.", + "\"There is only one place for Yahya Sinwar, and it is beside Mohammed Deif and the rest of the Oct. 7 terrorists,\" he told the Saudi state-owned Al-Arabiya television. \"That is the only place we're preparing and intending for him.\"", + "Amer, in Gaza, stressed the importance of diplomacy before military strength, particularly as negotiations continue between both sides.", + "\"There are rules to resistance, rules to war and rules to peace,\" said Amer. \"[And] we need peace in this current moment.\"" + ] + } + ] + }, + "publishing_date": "2024-08-08 08:00:00+00:00", + "title": "Palestinians say his appointment could ruin ceasefire talks", + "topics": [ + "World" + ] + } +} diff --git a/tests/resources/parser/test_data/ca/CBCNews_2024_08_08.html.gz b/tests/resources/parser/test_data/ca/CBCNews_2024_08_08.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..22aeece3e11d03f3263265bad637b4f2c541d04a GIT binary patch literal 49787 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 z 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 Date: Wed, 28 Aug 2024 00:01:56 +0200 Subject: [PATCH 2/5] redo ld parsing --- src/fundus/publishers/ca/cbc_news.py | 39 ++++++++++++------- .../parser/test_data/ca/CBCNews.json | 10 ++++- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/src/fundus/publishers/ca/cbc_news.py b/src/fundus/publishers/ca/cbc_news.py index 919a2c26..2ae47ab0 100644 --- a/src/fundus/publishers/ca/cbc_news.py +++ b/src/fundus/publishers/ca/cbc_news.py @@ -3,18 +3,21 @@ import re from typing import List, Optional +import lxml import more_itertools from lxml.cssselect import CSSSelector from lxml.etree import XPath from lxml.html import document_fromstring from fundus.parser import ArticleBody, BaseParser, ParserProxy, attribute +from fundus.parser.base_parser import Precomputed, logger from fundus.parser.data import LinkedDataMapping from fundus.parser.utility import ( extract_article_body_with_selector, generic_author_parsing, generic_date_parsing, generic_topic_parsing, + get_meta_content, ) @@ -23,8 +26,22 @@ class V1(BaseParser): _summary_selector = CSSSelector("h2.deck") _subheadline_selector = CSSSelector("div.story > h2") _paragraph_selector = CSSSelector("div.story > p") + _cbc_ld_selector: XPath = XPath("//script[@type='application/ld+json' or @id='initialStateDom']") - _author_ld_selector = XPath("//script[@id='initialStateDom']") + def _base_setup(self, html: str) -> None: + doc = lxml.html.document_fromstring(html) + ld_nodes = self._cbc_ld_selector(doc) + lds = [] + for node in ld_nodes: + try: + json_object = json.loads(re.sub(r"(window\.__INITIAL_STATE__ = |;$)", "", node.text_content())) + if not json_object.get("@type"): + json_object["@type"] = "FurtherDetails" + lds.append(json_object) + except json.JSONDecodeError as error: + logger.debug(f"Encountered {error!r} during LD parsing") + collapsed_lds = more_itertools.collapse(lds, base_type=dict) + self.precomputed = Precomputed(html, doc, get_meta_content(doc), LinkedDataMapping(collapsed_lds)) @attribute def body(self) -> ArticleBody: @@ -37,21 +54,11 @@ def body(self) -> ArticleBody: @attribute def authors(self) -> List[str]: - doc = document_fromstring(self.precomputed.html) - ld_nodes = self._author_ld_selector(doc) - try: - author_ld = json.loads(re.sub(r"(window\.__INITIAL_STATE__ = |;$)", "", ld_nodes[0].text_content())) - except json.JSONDecodeError: - return [] - if not (details := author_ld.get("detail")): - return [] - if not (content := details.get("content")): - return [] - return generic_author_parsing(content.get("authorList")) + return generic_author_parsing(self.precomputed.ld.bf_search("authorList")) @attribute def publishing_date(self) -> Optional[datetime.datetime]: - return generic_date_parsing(self.precomputed.ld.bf_search("ReportageNewsArticle")[0].get("datePublished")) + return generic_date_parsing(self.precomputed.ld.bf_search("datePublished")) @attribute def title(self) -> Optional[str]: @@ -61,4 +68,8 @@ def title(self) -> Optional[str]: @attribute def topics(self) -> List[str]: - return generic_topic_parsing(self.precomputed.ld.bf_search("ReportageNewsArticle")[0].get("articleSection")) + topic_dict = self.precomputed.ld.bf_search("keywords") + topic_list = [v.get("name") for v in topic_dict.get("tags")] + for v in topic_dict.get("concepts"): + topic_list.append(re.sub(r".*/", "", v.get("path"))) + return topic_list diff --git a/tests/resources/parser/test_data/ca/CBCNews.json b/tests/resources/parser/test_data/ca/CBCNews.json index b6632f46..b8a86b95 100644 --- a/tests/resources/parser/test_data/ca/CBCNews.json +++ b/tests/resources/parser/test_data/ca/CBCNews.json @@ -69,7 +69,15 @@ "publishing_date": "2024-08-08 08:00:00+00:00", "title": "Palestinians say his appointment could ruin ceasefire talks", "topics": [ - "World" + "Israel-Hamas war", + "Iran", + "Israel", + "Tehran", + "Hamas", + "Yahya Sinwar", + "Assassinations", + "Kidnapping", + "War and unrest" ] } } From 4371d6b77d04b93e9da3d9698614421bb188719f Mon Sep 17 00:00:00 2001 From: Max Dallabetta Date: Wed, 28 Aug 2024 16:59:13 +0200 Subject: [PATCH 3/5] rework `CBCNews` parser --- src/fundus/parser/data.py | 4 +-- src/fundus/parser/utility.py | 38 +++++++++++--------- src/fundus/publishers/ca/cbc_news.py | 53 ++++++++++++---------------- 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/src/fundus/parser/data.py b/src/fundus/parser/data.py index b9faea9d..6db1a341 100644 --- a/src/fundus/parser/data.py +++ b/src/fundus/parser/data.py @@ -53,8 +53,8 @@ def __init__(self, lds: Iterable[Dict[str, Any]] = ()): def serialize(self) -> Dict[str, Any]: return {attribute: value for attribute, value in self.__dict__.items() if "__" not in attribute} - def add_ld(self, ld: Dict[str, Any]) -> None: - if ld_type := ld.get("@type"): + def add_ld(self, ld: Dict[str, Any], name: Optional[str] = None) -> None: + if ld_type := ld.get("@type", name): if isinstance(ld_type, list): if len(ld_type) == 1: ld_type = ld_type[0] diff --git a/src/fundus/parser/utility.py b/src/fundus/parser/utility.py index 67810e25..6eb7b664 100644 --- a/src/fundus/parser/utility.py +++ b/src/fundus/parser/utility.py @@ -7,6 +7,7 @@ from datetime import datetime from functools import total_ordering from typing import ( + Any, Callable, ClassVar, Dict, @@ -155,6 +156,25 @@ def extract_nodes(selector: XPath, node_type: Type[Node]) -> List[Node]: _json_pattern = re.compile(r"(?P{[\s\S]*}|\[\s*{[\s\S]*}\s*](?!\s*}))") +def extract_json_from_dom(root: lxml.html.HtmlElement, selector: XPath) -> Iterable[Dict[str, Any]]: + def sanitize(text: str) -> Optional[str]: + # capture only content enclosed as follows: {...} or [{...}] + match = re.search(_json_pattern, text) + if match is not None and (sanitized := match.group("json")): + return sanitized + return None + + json_nodes = selector(root) + jsons = [] + for node in json_nodes: + json_content = sanitize(node.text_content()) or "" + try: + jsons.append(json.loads(json_content)) + except json.JSONDecodeError as error: + logger.debug(f"Encountered {error!r} during JSON parsing") + return more_itertools.collapse(jsons, base_type=dict) + + def get_ld_content(root: lxml.html.HtmlElement) -> LinkedDataMapping: """Parse JSON-LD from HTML. @@ -168,23 +188,7 @@ def get_ld_content(root: lxml.html.HtmlElement) -> LinkedDataMapping: The JSON-LD data as a LinkedDataMapping """ - def sanitize(text: str) -> Optional[str]: - # capture only content enclosed as follows: {...} or [{...}] - match = re.search(_json_pattern, text) - if match is not None and (sanitized := match.group("json")): - return sanitized - return None - - ld_nodes = _ld_node_selector(root) - lds = [] - for node in ld_nodes: - json_content = sanitize(node.text_content()) or "" - try: - lds.append(json.loads(json_content)) - except json.JSONDecodeError as error: - logger.debug(f"Encountered {error!r} during LD parsing") - collapsed_lds = more_itertools.collapse(lds, base_type=dict) - return LinkedDataMapping(collapsed_lds) + return LinkedDataMapping(extract_json_from_dom(root, _ld_node_selector)) _meta_node_selector = CSSSelector("head > meta, body > meta") diff --git a/src/fundus/publishers/ca/cbc_news.py b/src/fundus/publishers/ca/cbc_news.py index 2ae47ab0..75ae1fc3 100644 --- a/src/fundus/publishers/ca/cbc_news.py +++ b/src/fundus/publishers/ca/cbc_news.py @@ -1,23 +1,17 @@ import datetime -import json import re from typing import List, Optional -import lxml -import more_itertools from lxml.cssselect import CSSSelector from lxml.etree import XPath -from lxml.html import document_fromstring from fundus.parser import ArticleBody, BaseParser, ParserProxy, attribute -from fundus.parser.base_parser import Precomputed, logger -from fundus.parser.data import LinkedDataMapping +from fundus.parser.base_parser import function from fundus.parser.utility import ( extract_article_body_with_selector, + extract_json_from_dom, generic_author_parsing, generic_date_parsing, - generic_topic_parsing, - get_meta_content, ) @@ -26,22 +20,14 @@ class V1(BaseParser): _summary_selector = CSSSelector("h2.deck") _subheadline_selector = CSSSelector("div.story > h2") _paragraph_selector = CSSSelector("div.story > p") - _cbc_ld_selector: XPath = XPath("//script[@type='application/ld+json' or @id='initialStateDom']") - def _base_setup(self, html: str) -> None: - doc = lxml.html.document_fromstring(html) - ld_nodes = self._cbc_ld_selector(doc) - lds = [] - for node in ld_nodes: - try: - json_object = json.loads(re.sub(r"(window\.__INITIAL_STATE__ = |;$)", "", node.text_content())) - if not json_object.get("@type"): - json_object["@type"] = "FurtherDetails" - lds.append(json_object) - except json.JSONDecodeError as error: - logger.debug(f"Encountered {error!r} during LD parsing") - collapsed_lds = more_itertools.collapse(lds, base_type=dict) - self.precomputed = Precomputed(html, doc, get_meta_content(doc), LinkedDataMapping(collapsed_lds)) + _cbc_ld_selector: XPath = XPath("//script[@id='initialStateDom']") + + @function(priority=1) + def _parse_initial_state_dom(self): + state_dom_json = extract_json_from_dom(self.precomputed.doc, self._cbc_ld_selector) + for ld in state_dom_json: + self.precomputed.ld.add_ld(ld, "initialStateDom") @attribute def body(self) -> ArticleBody: @@ -54,7 +40,7 @@ def body(self) -> ArticleBody: @attribute def authors(self) -> List[str]: - return generic_author_parsing(self.precomputed.ld.bf_search("authorList")) + return generic_author_parsing(self.precomputed.ld.bf_search("author")) @attribute def publishing_date(self) -> Optional[datetime.datetime]: @@ -62,14 +48,19 @@ def publishing_date(self) -> Optional[datetime.datetime]: @attribute def title(self) -> Optional[str]: - if not (title := self.precomputed.meta.get("og:title")): - return title - return re.sub(r" \|.*", "", title) + return self.precomputed.ld.bf_search("headline") @attribute def topics(self) -> List[str]: - topic_dict = self.precomputed.ld.bf_search("keywords") - topic_list = [v.get("name") for v in topic_dict.get("tags")] - for v in topic_dict.get("concepts"): - topic_list.append(re.sub(r".*/", "", v.get("path"))) + if (topic_dict := self.precomputed.ld.bf_search("keywords")) is None: + return [] + + # add locations + topic_list = [topic for location in topic_dict.get("tags") if (topic := location.get("name")) is not None] + + # add subjects + for subject in topic_dict.get("concepts"): + if (path := subject.get("path")) is not None: + topic_list.append(re.sub(r".*/", "", path)) + return topic_list From 823114a91b38cebdda5e93b1068c0b6bf98819f3 Mon Sep 17 00:00:00 2001 From: Max Dallabetta Date: Wed, 28 Aug 2024 16:59:22 +0200 Subject: [PATCH 4/5] update test case --- tests/resources/parser/test_data/ca/CBCNews.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/resources/parser/test_data/ca/CBCNews.json b/tests/resources/parser/test_data/ca/CBCNews.json index b8a86b95..42959f31 100644 --- a/tests/resources/parser/test_data/ca/CBCNews.json +++ b/tests/resources/parser/test_data/ca/CBCNews.json @@ -67,7 +67,7 @@ ] }, "publishing_date": "2024-08-08 08:00:00+00:00", - "title": "Palestinians say his appointment could ruin ceasefire talks", + "title": "What's next for Gaza, after Yahya Sinwar's appointment as Hamas political head?", "topics": [ "Israel-Hamas war", "Iran", From ebb05e362fd216020e4e8bdff09555308f0e3549 Mon Sep 17 00:00:00 2001 From: Max Dallabetta Date: Tue, 3 Sep 2024 13:02:01 +0200 Subject: [PATCH 5/5] fix a bug with topic parsing --- src/fundus/publishers/ca/cbc_news.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fundus/publishers/ca/cbc_news.py b/src/fundus/publishers/ca/cbc_news.py index 75ae1fc3..fba874ab 100644 --- a/src/fundus/publishers/ca/cbc_news.py +++ b/src/fundus/publishers/ca/cbc_news.py @@ -52,7 +52,7 @@ def title(self) -> Optional[str]: @attribute def topics(self) -> List[str]: - if (topic_dict := self.precomputed.ld.bf_search("keywords")) is None: + if not (topic_dict := self.precomputed.ld.bf_search("keywords")): return [] # add locations