欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python爬虫学习(一)——爬取新浪新闻

程序员文章站 2022-05-02 22:03:35
...

参照网易云课堂的课程实践的结果:
准备工作:安装requests和BeautifulSoup4。打开cmd,输入如下命令

pip install requests
pip install BeautifulSoup4

打开我们要爬取的页面,这里以新浪新闻为例,地址为:http://news.sina.com.cn/china/
大概的思路就是通过浏览器的开发者工具检测到要获取数据所需要的request是什么,然后使用python的requests模块生成这样的调用,使用BeautifulSoup解析返回的response,根据页面元素提取相关信息,结构化,最后持久化。

代码如下:

import requests
from bs4 import BeautifulSoup
from datetime import datetime
import re
import json
import pymysql

#本示例通过爬取新浪中国的新闻来学习爬虫的基本操作

#获取一则新闻详情的方法
def getNewsDetail(newsurl):
    result = {}
    #使用requests模块方法向服务器发送请求,获取到response
    res = requests.get(newsurl)
    #设置编码格式为utf-8
    res.encoding = 'utf-8'
    #使用BeautifulSoup4将response解析成DOM对象
    soup = BeautifulSoup(res.text,'html.parser')
    #使用BeautifulSoup的select可以根据页面元素获取信息
    # "#XXX"是查找"id=XXX"的元素,返回一个list,[0]就是第一个元素,这时得到一个标签,text获取文本信息
    result['title'] = soup.select('#artibodyTitle')[0].text
    # "#XXX"是查找"class=XXX"的元素,返回一个list,[0]就是第一个元素,这时得到一个标签,标签相互嵌套,使用contents可以获取内部潜逃的标签列
    # strip()是string的去空白方法
    result['newssource'] = soup.select('.time-source')[0].contents[1].text.strip()
    timesource = soup.select('.time-source')[0].contents[0].strip()
    #使用datetime模块进行日期的处理
    #datetime.strptime将字符串按格式转化成datetime对象
    #strftime将datetime对象转化成格式化字符串
    result['date'] = datetime.strptime(timesource,"%Y年%m月%d日%H:%M").strftime("%Y-%m-%d %H:%M:%S")
    #这句话是多个语句复合而成的,
    #文章主题的标签始artibody,里面分成若干个断落,获取想要的段落,然后获取text,去空白(strip),得到一个string的list
    #然后用' '.join()将它们组合在一起,以空格隔开
    result['article'] = ' '.join([p.text.strip() for p in soup.select('#artibody p')[:-1]])
    #获取责任编辑的文本,然后使用lstrip()匹配删除掉左边的固定格式文字
    result['editor'] = soup.select('.article-editor')[0].text.lstrip('责任编辑:')
    #调用下面的获取评论数方法
    result['comments'] = getCommentCounts(newsurl)
    #返回一个字典对象
    return result

#经过页面元素分析,这个url就是新闻评论的模板url,
#注意那对大括号,实际上是对应新闻的id,方法中会使用它匹配生成真正的url
commentURLPattern = 'http://comment5.news.sina.com.cn/page/info?version=1&format=js&channel=gn&newsid=comos-' \
                    '{}' \
                    '&group=&compress=0&ie=utf-8&oe=utf-8&page=1&page_size=20'

#获取一则新闻评论数量的方法
def getCommentCounts(newsurl):
    #根据新闻id寻找对应的评论,所以首先获取新闻id
    #新闻id在新闻的url中有显示,使用re正则表达式,匹配获得id
    m = re.search('doc-i(.+).shtml', newsurl)
    #group(0)可以获取匹配的字符串,group(1)可以获取匹配字符串中不定的部分,即".+"的部分
    newsid = m.group(1)
    #format()方法生成新闻对应评论的url
    commenturl = commentURLPattern.format(newsid)
    #获取response
    comments = requests.get(commenturl)
    comments.encoding = 'utf-8'
    #response是一个json数据,使用json模块方法加载进来
    jd = json.loads(comments.text.strip('var data='))
    #找到对应的数据
    return jd['result']['count']['total']

#经过页面元素分析,这个url就是新闻分页展示的模板url,
#注意那对大括号,实际上代表一个数字,方法中会使用它匹配生成真正的url
URLPattern = 'http://api.roll.news.sina.com.cn/zt_list?channel=news&cat_1=gnxw&cat_2==gdxw1||=gatxw||=zs-pl||=mtjj&level==1||=2&show_ext=1&show_all=1&show_num=22&tag=1&format=json&page={}'

#根据url获取一个分页的新闻详情
def parseLinkList(url):
    newdetails = []
    res = requests.get(url)
    #根据url获取response后,去掉两边的js语句获得json数据
    jd = json.loads(res.text.lstrip('  newsloadercallback(').rstrip(');'))
    #json数据中包含着该分页中新闻列表信息
    for ent in jd['result']['data']:
        #注意调用上面的方法处理,然后加到结果集中
        newdetails.append(getNewsDetail(ent['url']))
    #返回结果
    return newdetails

# for i in range(1,10):
#     newsurl = URLPattern.format(i)
#     print(newsurl)

#人工设置爬取的分业范围,将爬取到的列表合并成一个列表
news_total = []
#设置爬取10个分页的新闻
for i in range(1,10):
    news = parseLinkList(URLPattern.format(i))
    news_total.extend(news)

#--------------------------------------

#以下是数据库操作

# 打开数据库连接
db = pymysql.connect(host = "localhost", user = "root", passwd = "root", db = "testdb", charset = "utf8")

# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()

# 使用预处理语句创建表
sql = """CREATE TABLE IF NOT EXISTS `news` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `title` varchar(50) DEFAULT NULL,
        `newssource` varchar(10) DEFAULT NULL,
        `date` DATETIME DEFAULT NULL,
        `article` LONGTEXT DEFAULT NULL,
        `editor` varchar(10) DEFAULT NULL,
        `comments` int(11) DEFAULT NULL,
        KEY `Index 1` (`id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8 """

cursor.execute(sql)

#对于爬取到的数据,逐条插入数据库
for newsdata in news_total:
    # SQL 插入语句
    sql = "INSERT INTO NEWS(TITLE,NEWSSOURCE,DATE,ARTICLE,EDITOR,COMMENTS) \
       VALUES ('%s','%s','%s','%s','%s','%d')" % \
       (newsdata['title'],newsdata['newssource'],newsdata['date'],newsdata['article'],newsdata['editor'],newsdata['comments'])
    try:
        # 执行sql语句
        cursor.execute(sql)
        # 提交到数据库执行
        db.commit()
    except:
        # 如果发生错误则回滚
        db.rollback()

# 关闭数据库连接
db.close()

#也可以使用pandas做数据展示和持久化
# import pandas
# df = pandas.DataFrame(news_total)
# df.head(10)