parser.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. def __init__(self,url,source,lang="en"):
  56. url=sanitizeUrl(url)
  57. self.URL=url
  58. self.SOURCE=source
  59. a = Article(url, language=lang,keep_article_html=True)
  60. a.download()
  61. try:
  62. a.parse()
  63. except newspaper.article.ArticleException:
  64. print("ERROR parsing article")
  65. ERROR=True
  66. return
  67. a.nlp()
  68. a.authors=self.dedup([self.fix_author(value) for value in a.authors if not self.isComment(value) and not self.isNoise(value)])
  69. self.SCRAP_DATE=datetime.datetime.now()
  70. self.AUTHORS=a.authors
  71. self.PUBLISH_DATE=a.publish_date
  72. self.SUMMARY=a.summary
  73. self.TEXT=a.text
  74. self.HTML=htmlmin.minify(a.article_html,remove_empty_space=True)
  75. def get(self):
  76. return {
  77. "url": self.URL,
  78. "source": self.SOURCE,
  79. "authors": self.AUTHORS,
  80. "scrap_date": self.SCRAP_DATE,
  81. "publish_date": self.PUBLISH_DATE,
  82. "summary": self.SUMMARY,
  83. "text": self.TEXT,
  84. "html": self.HTML
  85. }
  86. def dedup(self,val):
  87. return list(set(val))
  88. def isNoise(self,v):
  89. if self.r_noise.match(v):
  90. return True
  91. return False
  92. def isComment(self,v):
  93. if self.r.match(v):
  94. return True
  95. return False
  96. def fix_author(self,a):
  97. return a.replace("_", " ").lower()
  98. unsharer=re.compile(r"(\?share=|#).+$",flags=re.IGNORECASE)
  99. def sanitizeUrl(url):
  100. global unsharer
  101. ret = re.sub(unsharer, "",url)
  102. return ret
  103. def sanitizeSelector(s):
  104. s=s.replace(">a","> a")
  105. if re.match(r'=\w+\]',s) is None:
  106. return s
  107. ret=re.sub(r"=(\w+)]", r'="\1"]', s)
  108. return ret
  109. def parseSource(s):
  110. sel=None
  111. rss=False
  112. if "selector" in s:
  113. sel=s["selector"]
  114. if "rss" in s:
  115. rss=bool(["rss"])
  116. l=LinkList(s["link"],sel,rss=rss)
  117. if l is not None:
  118. print("[Source] %s: %d total articles" % ( s["name"], len(l.links)))
  119. for a in l.links:
  120. if not d.article_exists(a):
  121. q.put(a)
  122. print("[Source] %s: %d new articles" % ( s["name"], q.qsize()))
  123. if not q.empty():
  124. threads=[]
  125. for i in range(num_worker_threads):
  126. t = threading.Thread(target=news_worker,args=[s["_id"]])
  127. t.daemon=True
  128. t.start()
  129. threads.append(t)
  130. q.join()
  131. for i in range(num_worker_threads):
  132. q.put(None)
  133. for t in threads:
  134. t.join()
  135. print("[Source] %s => Finished parsing" %s["name"])
  136. def news_worker(source_id):
  137. while True:
  138. item = q.get()
  139. if item is None:
  140. break
  141. n=News(item,source_id)
  142. print("[Thread %s] %s" % (threading.current_thread().name,n.URL))
  143. d.insert_article(n.get())
  144. q.task_done()
  145. q=Queue()
  146. d=db()
  147. num_worker_threads=20
  148. for s in d.sources():
  149. parseSource(s)