今天来试着把我们从列表页开始,一路抓到的文章内容和信息都存到数据库中!
突然想到还没整理过完整流程的原代码,先来整理一下呗~
import requests
from bs4 import BeautifulSoup
from datetime import datetime
import re
def crawl_list():
"""爬取文章列表页
"""
# 抓取 1~10 页
for page in range(1, 11):
html_doc = requests.get(f'https://ithelp.ithome.com.tw/articles?tab=tech&page={page}').text
soup = BeautifulSoup(html_doc, 'lxml')
# 先找到文章区块
article_tags = soup.find_all('div', class_='qa-list')
# 没有文章
if len(article_tags) == 0:
# 跳出换页循环或离开程序
print('没有文章了!')
break
for article_tag in article_tags:
# 再由每个区块去找文章链接
title_tag = article_tag.find('a', class_='qa-list__title-link')
article_url = title_tag['href']
crawl_content(article_url)
def crawl_content(url):
"""爬取文章内容
:param url: 文章链接
"""
html_doc = requests.get(url).text
soup = BeautifulSoup(html_doc, 'lxml')
leftside = soup.find('div', class_='leftside')
original_post = leftside.find('div', class_='qa-panel')
article_header = original_post.find('div', class_='qa-header')
article_info = article_header.find('div', class_='ir-article-info__content')
# 标题
title = article_header.find('h2', class_='qa-header__title').get_text(strip=True)
# 作者
author = article_info.find('a', class_='ir-article-info__name').get_text(strip=True)
# 发文时间
published_time_str = article_info.find('a', class_='ir-article-info__time').get_text(strip=True)
published_time = datetime.strptime(published_time_str, '%Y-%m-%d %H:%M:%S')
# 文章标签
tag_group = article_header.find('div', class_='qa-header__tagGroup')
tags_element = tag_group.find_all('a', class_='tag')
tags = [tag_element.get_text(strip=True) for tag_element in tags_element]
# 内文
content = original_post.find('div', class_='markdown__style').get_text(strip=True)
# 浏览数
view_count_str = article_info.find('div', class_='ir-article-info__view').get_text(strip=True)
view_count = int(re.search('(\\d+).*', view_count_str).group(1))
article = {
'url': url,
'title': title,
'author': author,
'publish_time': published_time,
'tags': tags,
'content': content,
'view_count': view_count
}
print(article)
if __name__ == '__main__':
crawl_list()
把上面这段代码存成 ithome_crawler.py
文件后,进入虚拟环境运行。
因为代码越来越长,决定之后都存成
.py
文件后再运行了
在 crawl_content(url)
方法的最后,直接把每篇文章都 print 出来,如果要把数据存进数据库,就从这边开始做修改吧!
因为目的地数据库可能会有不同环境,例如从 PostgreSQL 变成 MySQL,或者变成后面几天会提到的 MongoDB,所以这边可以把与数据库有关的逻辑独立成一个方法,跟原本爬取的逻辑分开来,未来比较好维护。
import psycopg2
host = 'localhost'
user = 'postgres'
dbname = 'ithome2019'
password = '<server_admin_password>'
conn_string = f'host={host} user={user} dbname={dbname} password={password}'
conn = psycopg2.connect(conn_string)
print('数据库连接成功!')
cursor = conn.cursor()
def crawl_content(url):
"""爬取文章内容
:param url: 文章链接
"""
# ...略
insert_db(article)
def insert_db(article):
"""把文章插入到数据库中
:param article: 文章数据
"""
cursor.execute('''
INSERT INTO public.ithome_article(title, url, author, publish_time, tags, content)
VALUES (%(title)s,%(url)s,%(author)s,%(publish_time)s,%(tags)s,%(content)s);
''',
article)
print(f'[{article["title"]}] 添加成功!')
conn.commit()
cursor.close()
conn.close()
突然发现少开一个浏览数
的字段,赶快补上去吧!
在要加字段的数据表上按「右键 > Create > Column」,填入对应的信息后按「Save」。
修改原本的 insert 语法后再运行,就可以看到刚刚抓下来的这些数据啰!
def insert_db(article):
"""把文章插入到数据库中
:param article: 文章数据
"""
cursor.execute('''
INSERT INTO public.ithome_article(title, url, author, publish_time, tags, content, view_count)
VALUES (%(title)s,%(url)s,%(author)s,%(publish_time)s,%(tags)s,%(content)s,%(view_count)s);
''',
article)
print(f'[{article["title"]}] 添加成功!')
conn.commit()
今天的完整代码有放在 gist 上了,有兴趣的读者可以上去看看。
明天会接着把回文的数据也存到数据库中~