analyze news feed, the first try

TITIPATA bio photo

By TITIPATA

ชอบฟังเพลงอินดี้และอัลเทอร์เนทีฟ เวลาว่างชอบเขียนโปรแกรมและเล่นเกม

Email Twitter Google+ Github

Reading time ~25 minutes

TITIPATA bio photo

By TITIPATA

ชอบฟังเพลงอินดี้และอัลเทอร์เนทีฟ เวลาว่างชอบเขียนโปรแกรมและเล่นเกม

Email Twitter Google+ Github

สำหรับโพสต์ที่แล้ว เราได้ลองใช้ฟังก์ชันง่ายๆใน Python เพื่อช่วยในการวิเคราะห์ไลน์แชทกันไป ในโพสต์นี้เราจะมาลองเขียน Python snippet เพื่อใช้วิเคราะห์ news articles กันบ้าง ต้องบอกไว้ก่อนว่าเราไม่ได้ลองโหลดบทความมามากมาย แต่หวังว่าผู้อ่านจะได้เรียนรู้ library หลายๆอย่างบน Python และสามารถ เอามาใช้งานได้ในอนาคต เพื่อว่าหลังจากอ่านโพสต์นี้ ผู้อ่านจะเอาไปต่อยอดได้ง่ายขึ้น

ก่อนเราจะลุยไปถึงโค้ดกันนั้น เรามาดูกันก่อนว่า tools หรือ library ที่เราจะใช้กันวันนี้มีอะไรบ้างตามลำดับ

  • newspaper ใช้ในการดาวน์โหลดข่าวและลิงค์ของข่าวในหน้าหลัก
  • BeautifulSoup ใช้ในการ scrape website หรือดึงข้อมูลมากจากเว็บไซต์นั่นเอง
  • scikit-learn สำหรับ machine learning algorithm สำหรับโพสต์นี้เราจะใช้ Principal component analysis เพื่อลดจำนวนมิติของเวกเตอร์ของคำศัพท์ที่อยู่ในบทความ (dimensionality reduction) ซึ่งทำให้เราพล็อตกราฟออกมาสวยงาม ไม่ยุ่งเหยิง
  • bokeh เป็น plotting library คล้ายกับ matplotlib แต่สำหรับโพสต์นี้ เราต้องการเขียน interactive plot สำหรับการพล็อต เราเลยเลือกใช้ bokeh library

สำหรับขั้นตอนของการเขียนโปรเจกต์ เราจะแบ่งเป็นสามขั้นตอนหลักๆดังต่อไปนี้

  • เก็บข้อมูล (collect the data)
  • ประมวลผล (เพื่อแปลง text จากข่าวให้เป็นเมกทริกซ์ที่เราสามารถพล็อตได้)
  • พล็อต (visualization)

สิ่งที่เราจะเขียนในโพสต์นี้เป็นเพียง 1 กิ่งก้านของ “How to learn Machine Learning Roadmap in 6 months” ที่แนะนำโดย scikit-learn เท่านั้น ไว้โพสต์หน้าๆ เราจะมาหาอะไรทำสนุกๆต่อ แต่ตอนนี้เรามาเริ่มกันเลยแล้วกัน

"How to learn Machine Learning Roadmap in 6 month" (credit: scikit-learn.org)

1. เก็บข้อมูล

ในกระบวนการทั้งหมดที่กล่าวมาข้างต้น การเก็บข้อมูลน่าจะเป็นกระบวนการที่ยากที่สุดแล้วก็เป็นได้ ในที่นี้เราใช้ไลบรารี่ชื่อ newspaper ไลบรารี่นี้มีคำสั่งที่ชื่อว่า build เพื่อให้เราเก็บลิงค์ทั้งหมดบนเพจนั้นๆได้ หลังจากเราโหลดลิงค์จากหน้าแรกมาแล้ว จะสามารถใช้คำสั่ง download เพื่อโหลดเนื้อหาข่าวของแต่ละบทความได้

เราเริ่มด้วยเว็บไซต์โปรดของเราเลย Wired.com

import newspaper
from newspaper import Article

wired_paper = newspaper.build('http://www.wired.com')
wired_urls = [u for u in wired_paper.article_urls() if 'https://www.wired.com/' in u]
wired_articles = []
for i, wired_url in enumerate(wired_urls):
    wired_article = Article(wired_url)
    wired_articles.append(wired_article)
for w in wired_articles:
    w.download() # load all articles

สำหรับอีกเพจที่เราชอบมากก็คือ Engadget แต่เนื่องจากว่าเราไม่สามารถใช้ฟังค์ชัน download เพื่อโหลดเนื้อหาข่าวหลังจากเก็บแต่ละ Article มาได้ ก็เลยต้องใช้ BeautifulSoup เพื่อโหลดเนื้อหาข่าวจาก Engadget แทน หน้าตาของโค้ดเป็นตามด้านล่าง

import requests
from bs4 import BeautifulSoup

engadget_paper = newspaper.build('http://www.engadget.com/')
engadget_urls = [u for u in engadget_paper.article_urls() if 'video' not in u and 'tumblr' not in u]
engadget_contents = []
for url in engadget_urls:
    r  = requests.get(url)
    engadget_contents.append([url, r.text])

r.text ในที่นี้คือ HTML ทั้งหมดจากแต่ละลิงค์เพจที่เราเก็บมาได้ เราต้องเขียนโค้ด BeautifulSoup เพิ่มเติมเล็กน้อยเพื่อเก็บแท็กจากข่าว หัวข้อข่าว และเนื้อหาข่าว

def get_engadget_news_from_content(content):
    soup = BeautifulSoup(content, "lxml")
    title = []
    tags = []
    for s in soup.find_all('meta'):
        if s.get('property') == 'og:title':
            title = s.get('content', '')
        elif s.get('name') == 'tags':
            tags.append(s.get('content'))
    body_text = ' '.join([p.text for p in soup.find_all('p') if p.get('class') is None])
    return {'title': title,
            'tag': ';'.join(tags),
            'text': body_text}

หลังจากเราได้ข่าวจากทั้งสองแหล่งแล้ว แปลงเป็น dataframe และต่อ dataframe เข้าไปด้วยกัน

import pandas as pd
engadget_df.loc[:, 'source'] = 'engadget'
wired_df.loc[:, 'source'] = 'wired'
tech_news_df = pd.concat((wired_df, engadget_df)) # ต่อ dataframe เข้าด้วยกัน

จากตรงนี้ ใครขี้เกียจโหลดข่าวด้วยตัวเอง เราได้แปะ CSV (comma separated) file มาให้ด้วยที่

tech_news.csv

2. ประมวลผลข่าวที่ดาวน์โหลดมา

หลังจากเราได้ข่าวมาแล้วจากทั้งสองแหล่ง สิ่งที่เราจะทำถัดไปคือเราจะเก็บเฉพาะหัวข้อข่าวและ 3 ย่อหน้าแรก

def sample_paragraph(text):
    paragraph = ' '.join([a for a in text.split('\n') if a.strip() != ''][0:4])
    return paragraph
text_preproc = (tech_news_df.title + ' ' + tech_news_df.text.map(sample_paragraph)).map(lambda x: x.lower())

หลังจากนั้นใช้ TfidfVectorizer และ PCA จาก scikit-learn ไลบรารี่เพื่อแปลงข่าวทั้งหมดที่เรามีให้เหลือ 2 dimensions (เดี๋ยวว่างๆแล้วเราจะมาเขียนอธิบายเพิ่มให้อีกทีว่ามันคืออะไร)

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import PCA

tfidf_model = TfidfVectorizer(ngram_range=(1,2), max_df=0.8, min_df=1)
X = tfidf_model.fit_transform(text_preproc)
pca_model = PCA(n_components=2, whiten=True)
X_pca = pca_model.fit_transform(X.toarray())

X_pca ของเรามีขนาดเท่ากับ [n_article x 2] หรือเท่ากับ [150 x 2] นั่นเอง แต่ละแถวของ X_pca คือข่าวที่ได้ถูกแปลงเป็นแค่ตัวเลขสองตัวเท่านั้น

3. พล็อต

ท้ายสุดแล้ว เราเหลือแค่พล็อตข้อมูลที่เราประมวลผลมา ในที่นี้เราจะเลือกใช้ไลบรารี่ชื่อ bokeh เพื่อพล็อตข้อมูลที่เรามี หน้าตาของพล็อตแบบนี้บางทีเรียกว่า Scatter plot นะเออ

from bokeh.plotting import figure, show, ColumnDataSource
from bokeh.models import HoverTool

source = ColumnDataSource(tech_news_df)
# ใช้สีส้มถ้าเป็น Engadget และสีฟ้าถ้าเป็น the Wired
colors = tech_news_df['source'].map(lambda x: '#42b3f4' if x == 'wired' else '#f4a041')
hover = HoverTool(
        tooltips=[
            ("title", "@title"),
            ("url", "@url"),
            ("source", "@source")
        ]
    )
p = figure(plot_width=500, plot_height=800,
           tools=[hover],
           x_axis_label='1st Principal component',
           y_axis_label='2nd Principal component')
p.circle('x', 'y', size=8, source=source,
         fill_color=colors, fill_alpha=0.6,
         line_color=None)
show(p)

และนี่คือพล็อตแบบ interactive (เฉพาะบนคอมนะ)

หลังจากนั้นเราก็จะได้พล็อตออกมา และเมื่อเราลากเมาส์ผ่านไปบนพล็อต จุดที่เราลากผ่านจะโชว์หัวข้อข่าว ลิงค์ไปยังข่าว และที่มาของข่าวให้เรา จุดข้างบนสามจุดนั้นคือเรื่องเกี่ยวกับ SpaceX ทั้งหมด กลุ่มข่าวด้านล่างนี่ เกี่ยวกับ Spotify และ Apple Music ส่วนข้างขวาเกี่ยวกับ Gadget เป็นหลัก

จะเห็นได้ว่าเพียงแค่ 150 ข่าว เราก็สามารถเริ่มจัดกลุ่มให้ข่าวพวกนี้ได้แล้ว ถ้ามีข่าวเพิ่มอีกมากๆ ก็ยังทำได้เช่นกัน :)

อารัมภบท

ออกจะยากไปหน่อยสำหรับโพสต์นี้ แต่ว่า สำหรับใครที่สนใจ data science เบื้องต้น ลองทำตามโพสต์นี้ ลอง download, clean data ซักหน่อย แล้วพล็อตข้อมูลที่เรามี เป็นแบบฝึกหัดที่ทำให้เราเล่นกับ data ได้อย่างสนุกขึ้นในอนาคตนะ

เด็กหัวร้อนวิเคราะห์ PUBG

อย่างงี้ก็ได้หรอ? เมื่อความเนิร์ดอยู่เหนือความเกรียน วิเคราะห์ PUBG 700,000 เกม นับเป็นผู้เสียชีวิตกว่า 67 ล้านราย

Analysis of Thai pop songs' lyrics

Published on April 10, 2018