parser.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. #!/usr/bin/python3
  2. import sys
  3. import re
  4. import newspaper
  5. import pymongo
  6. import htmlmin
  7. import feedparser
  8. import datetime
  9. import threading
  10. from queue import Queue
  11. from frontpage import FrontPage
  12. from newspaper import Article
  13. from db import db
  14. from source import Source
  15. class LinkList():
  16. def __init__(self,url,selector=None,rss=False):
  17. self.links=[]
  18. if rss:
  19. feed = feedparser.parse(url)
  20. if feed["bozo"]:
  21. print("RSS ERROR. PANIC")
  22. #print(feed["bozo"]) FIXME: If bozo==1 => error
  23. self.links=[i["link"] for i in feed["items"]]
  24. print(self.links)
  25. else:
  26. if selector is None:
  27. #s=newspaper.build(url,language="en",memoize_articles=False) #memoize puede salvarme la vida
  28. s = newspaper.Source(url,memoize_articles=False)
  29. s.download()
  30. s.parse()
  31. s.set_categories()
  32. s.download_categories()
  33. s.parse_categories()
  34. s.generate_articles()
  35. self.links=[a.url for a in s.articles]
  36. else:
  37. f=FrontPage(url,selector)
  38. self.links=f.links
  39. self.links=self.purgeLinks(self.links)
  40. self.links=list(set(self.links)) #avoid dupes
  41. def purgeLinks(self,l):
  42. return [ sanitizeUrl(link) for link in l if not "presslist" in link.lower() and not "videolist" in link.lower() ]
  43. class News():
  44. r=re.compile(r"hours? ago|yesterday|today|last week|month",flags=re.IGNORECASE)
  45. r_noise=re.compile(r"posted|Product|Google",flags=re.IGNORECASE)
  46. URL=""
  47. AUTHORS=[]
  48. SCRAP_DATE=None
  49. PUBLISH_DATE=None
  50. SUMMARY=""
  51. TEXT=""
  52. HTML=""
  53. ERROR=False
  54. SOURCE=""
  55. WEIGHT_SUM=0
  56. MATCHES=False
  57. MATCHING_KW=[]
  58. def __init__(self,url,source,kw,lang="en"):
  59. url=sanitizeUrl(url)
  60. self.URL=url
  61. self.SOURCE=source
  62. a = Article(url, language=lang,keep_article_html=True)
  63. try:
  64. a.download()
  65. a.parse()
  66. except newspaper.article.ArticleException:
  67. print("ERROR parsing article")
  68. ERROR=True
  69. return
  70. except Exception:
  71. print("ERROR downloading article")
  72. ERROR=True
  73. return
  74. a.nlp()
  75. a.authors=self.dedup([self.fix_author(value) for value in a.authors if not self.isComment(value) and not self.isNoise(value)])
  76. self.SCRAP_DATE=datetime.datetime.now()
  77. self.AUTHORS=a.authors
  78. self.PUBLISH_DATE=a.publish_date
  79. self.SUMMARY=a.summary
  80. self.TEXT=a.text
  81. self.HTML=htmlmin.minify(a.article_html,remove_empty_space=True)
  82. for k in kw:
  83. if re.match(k["pattern"], self.TEXT):
  84. self.WEIGHT_SUM+=k["weight"]
  85. self.MATCHING_KW.append(k["kw"])
  86. self.MATCHES=True
  87. def get(self):
  88. return {
  89. "url": self.URL,
  90. "source": self.SOURCE,
  91. "authors": self.AUTHORS,
  92. "scrap_date": self.SCRAP_DATE,
  93. "publish_date": self.PUBLISH_DATE,
  94. "summary": self.SUMMARY,
  95. "text": self.TEXT,
  96. "matches": self.MATCHES,
  97. "weight": self.WEIGHT_SUM,
  98. "kw": self.MATCHING_KW,
  99. "html": self.HTML
  100. }
  101. def dedup(self,val):
  102. return list(set(val))
  103. def isNoise(self,v):
  104. if self.r_noise.match(v):
  105. return True
  106. return False
  107. def isComment(self,v):
  108. if self.r.match(v):
  109. return True
  110. return False
  111. def fix_author(self,a):
  112. return a.replace("_", " ").lower()
  113. unsharer=re.compile(r"(\?share=|#).+$",flags=re.IGNORECASE)
  114. def sanitizeUrl(url):
  115. global unsharer
  116. ret = re.sub(unsharer, "",url)
  117. return ret
  118. def sanitizeSelector(s):
  119. s=s.replace(">a","> a")
  120. if re.match(r'=\w+\]',s) is None:
  121. return s
  122. ret=re.sub(r"=(\w+)]", r'="\1"]', s)
  123. return ret
  124. def parseSource(s):
  125. sel=None
  126. rss=False
  127. if "selector" in s:
  128. sel=s["selector"]
  129. if "rss" in s:
  130. rss=bool(["rss"])
  131. l=LinkList(s["link"],sel,rss=rss)
  132. kw=d.get_keywords(s["category"])
  133. if l is not None:
  134. print("[Source] %s: %d total articles" % ( s["name"], len(l.links)))
  135. for a in l.links:
  136. if not d.article_exists(a):
  137. q.put(a)
  138. print("[Source] %s: %d new articles" % ( s["name"], q.qsize()))
  139. if not q.empty():
  140. threads=[]
  141. for i in range(num_worker_threads):
  142. t = threading.Thread(target=news_worker,args=[s["_id"],kw])
  143. t.daemon=True
  144. t.start()
  145. threads.append(t)
  146. q.join()
  147. for i in range(num_worker_threads):
  148. q.put(None)
  149. for t in threads:
  150. t.join()
  151. print("[Source] %s => Finished parsing" %s["name"])
  152. def news_worker(source_id,kw):
  153. while True:
  154. item = q.get()
  155. if item is None:
  156. break
  157. n=News(item,source_id,kw)
  158. print("[Thread %s] %s" % (threading.current_thread().name,n.URL))
  159. d.insert_article(n.get())
  160. q.task_done()
  161. q=Queue()
  162. d=db()
  163. num_worker_threads=20
  164. for s in d.sources():
  165. parseSource(s)