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

Python爬取新浪英超曼联文章内页--bs4,json,txt和csv以及编码

程序员文章站 2022-05-01 18:39:07
这个页面是新浪英超曼联新闻的首页,直接用lxml爬取,得到它是动态的爬不到,所以要考虑它的信息处理机制,使用Chrome审查元素,在Network--Priview中可以找到它的j...

这个页面是新浪英超曼联新闻的首页,直接用lxml爬取,得到它是动态的爬不到,所以要考虑它的信息处理机制,使用Chrome审查元素,在Network--Priview中可以找到它的json源。通常可以把&callbacak=直到最后删除,之前的信息似乎没什么用处,有用的时候再说。

之后使用json解析。

必须仔细观察json的结构,而且还需要不断地断点调试。

import requests

import json

headersIm = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}

#network headers里面

url_sport = "https://interface.sina.cn/pc_zt_roll_news_index.d.html?&subjectID=64558&channel=sports"

# json_url = 'https://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page=1&channel=sports&callback=jsonpcallback1483559621857'

json_url = 'https://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page=1&channel=sports'

jsContent = requests.get(json_url, headers = headersIm).content

print jsContent

jsDict = json.loads(jsContent)

jsResult = jsDict['result']

jsData = jsResult['data']

for each in jsData:

print each['title']

今天的结果:

穆帅示爱曼联球迷+球队 他终成“快乐的一个”?

7战6球!伊布当选英超月最佳 力压切尔西王牌

曼联大将自晒缝针照片 穆帅手下他已成红魔枢纽

打脸!谁说穆里尼奥会毁了曼联两大妖王?

曝穆帅欲签一昔日爱将 在皇马切尔西都用过他

......

虽然爬取网页信息,爬的不多,但是通常有两大方法:一个是从网页本身入手用lxml定位相应的h5元素,另一个是直接找信息源,通常是json。

又运行了一下程序,得到的结果是:

曼联猎物正式提交转会申请 同胞丰特或驰援鸟叔

曼联宣布12月最佳球员 伊布力压两核心首当选......

证明这个爬虫确实是爬取动态网站的。

没做完的部分:将标题,内容都整理好,只爬取前20条信息就足够了(或者加上后面10页的内容),进入内页后,将文章的文字部分也提取出来。

一、问题:

显然,只爬取标题是不够的,最主要的还是url链接,内页的正文:

foreach injsData:
    print each['title']+" "+each['url']

显然,所有有用的信息都已经在jsData这个列表里面,而现在为止,有两个问题:

1. 下一页的数据如何爬取;

2. 新闻内页的文字如何爬取。

二、分析

首先,下一页的数据。点击底下的页码的时候,数据变化,而浏览器地址栏是不变的,所以还是要从json源的地址入手。而通过观察json源地址:https://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page=1&channel=sports可知,page=1,这个1很可能是相应页码,同时,共114页,我们只取前20。

for i in range(1, 21):
    print i

所以可以构造一个函数专门来读取标题+日期+正文。

以页面:https://sports.sina.com.cn/g/pl/2017-01-07/doc-ifxzkfuk2454395.shtml为例,可知想得到的信息分别是:

标题:<h1id="artibodyTitle">欧文:穆帅别抱怨裁判专注比赛吧瓜帅没必要改变</h1>

           title_news =bsObj.find("h1",{"id":"artibodyTitle"}).string     #获取h1标签的内容

日期:<spanid="pub_date">2017年01月07日07:01</span>

           date_news =bsObj.find("span", {"id":"pub_date"}).string       #同上

内容:<divclass="BSHARE_POP blkContainerSblkCon clearfixblkContainerSblkCon_14" id="artibody">下的所有p标签的内容。

           content_news =bsObj.find("",{"id":"artibody"}).findAll("p")   #先找到id为artibody的元素,再向下寻找p标签

    for content in content_news:

        print content.get_text()                    #获得每一个p标签的内容

三、构造

至此,这个函数已写完,解析新闻内页的函数:

def getNewsContent(url_news):
    html_WebPage = requests.get(url_news,headers = headersIm)
    html_WebPage.encoding = 'utf-8'
    html_WebText = html_WebPage.text
    bsObj = BeautifulSoup(html_WebText, 'lxml')     #shtml的解析使用lxml方式
    title_news = bsObj.find("h1",{"id":"artibodyTitle"}).string  #获取h1标签的内容
    date_news = bsObj.find("span", {"id": "pub_date"}).string       #同上
    content_news = bsObj.find("", {"id":"artibody"}).findAll("p")   #先找到id为artibody的元素,再向下寻找p标签
    for content in content_news:
        print content.get_text()      #获得每一个p标签的内容

像这种:https://k.sina.cn/article_5695956221_1538164fd02000165x.html?cre=aspect&mod=2L&loc=27&r=0&doct=0&rfunc=39&tj=none&s=0&from=sports&vt=4就不解析了,所以还要用到Python的try...except。另外就是把标题,时间,内容,都存储为csv和txt,后面我分别写。

然而,获得的标题,时间,内容,经过测试,都是NavigableString类型的,需要把这种类型写入txt。只要在这种对象后面加上.encode('utf-8')即可转换:title_news = bsObj.find("h1",{"id":"artibodyTitle"}).string.encode('utf-8')

四、写入txt

最终的程序,把文章标题,日期,和内容存储在txt文档中(爬小说像不像):

# encoding=utf-8 注释可用中文
import requests
import json
from bs4 import BeautifulSoup

headersIm = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
f = open("news.txt", 'a')
def getNewsContent(url_news):
    html_WebPage = requests.get(url_news,headers = headersIm)
    html_WebPage.encoding = 'utf-8'
    html_WebText = html_WebPage.text
    bsObj = BeautifulSoup(html_WebText, 'lxml')     #shtml的解析使用lxml方式
    try:
        title_news = bsObj.find("h1",{"id": "artibodyTitle"}).string.encode('utf-8')     #获取h1标签的内容,.encode('utf-8')转换为str
        date_news = bsObj.find("span", {"id": "pub_date"}).string.encode('utf-8')       #同上
        content_news = bsObj.find("", {"id":"artibody"}).findAll("p")   #先找到id为artibody的元素,再向下寻找p标签
        f.write(title_news.rstrip()+'----'+date_news.lstrip())  #将标题和日期用----连接,并删除标题右侧和日期左侧的空格回车等,规范格式 写入
        for content in content_news:
            news_text = content.get_text().encode('utf-8')                    #获得每一个p标签的内容,转换为str
            f.write(news_text)                    #写入txt
            f.write('\n')            #文章末尾加换行符,区别两块新闻
    except:
        pass
    finally:
        pass

def newsListPages(urlPages):
    jsContent = requests.get(urlPages, headers=headersIm).content   #requsets解析文章列表
    jsDict = json.loads(jsContent)        #将内容用json库解析
    jsResult = jsDict['result'] 
    jsData = jsResult['data']
    for each in jsData:
        newsUrl = each['url']   #获得相应文章的url
        getNewsContent(newsUrl) #函数解析文章内页内容

for i in range(1, 3):
    url_json = 'https://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page='+str(i)+'&channel=sports'
        #构造json数据源的地址
    newsListPages(url_json)

f.close()

原理:分析从上至下,构建从下至上。首先分析树状结构,之后再构建解析文章—解析文章列表。但是,这样单线程似乎有点慢,如果用多线程的方式,首先需要构建文章列表,再用函数进行统一解析。

结果:

尴尬!鲁尼找对手换球衣被拒 来自曼城的恨(图)----2017年01月08日01:11

新浪体育讯  曼联4-0大胜雷丁一战,对于鲁尼本人意义非凡,此役他追平了队史射手王查尔顿爵士249球的纪录。按理说,这样一场比赛,谁都想沾沾鲁尼的喜气,但是雷丁后卫乔治-埃文斯便是个另类,他拒绝了鲁尼主动交换球衣的请求,原因是什么呢?

五、写入csv

写入csv格式类似:

# encoding=utf-8 注释可用中文
import requests
import json
from bs4 import BeautifulSoup
import csv
import codecs  #保证csv能正确写入的库

headersIm = {'User-Agent':'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36'}
csvfile = file('csv_test.csv', 'wb')    #创建csv文件,并准备写入
csvfile.write(codecs.BOM_UTF8)  #csv写入中文必备
writer = csv.writer(csvfile)    #构建写入对象
writer.writerow(['标题', '日期', '内容'])
def getNewsContent(url_news):
    html_WebPage = requests.get(url_news,headers = headersIm)
    html_WebPage.encoding = 'utf-8'
    html_WebText = html_WebPage.text
    bsObj = BeautifulSoup(html_WebText, 'lxml')     #shtml的解析使用lxml方式
    try:
        title_news = bsObj.find("h1",{"id": "artibodyTitle"}).string.encode('utf-8')     #获取h1标签的内容,.encode('utf-8')转换为str
        date_news = bsObj.find("span", {"id": "pub_date"}).string.encode('utf-8')       #同上
        content_news = bsObj.find("", {"id":"artibody"}).findAll("p")   #先找到id为artibody的元素,再向下寻找p标签
        content_str = ''
        for content in content_news:
            news_text = content.get_text().encode('utf-8')                    #获得每一个p标签的内容,转换为str
            content_str += news_text      #将文章正文存储在相应字符串中
        writer.writerow([title_news, date_news, content_str])   #构建每一行的格式,并写入
    except:
        pass
    finally:
        pass

def newsListPages(urlPages):
    jsContent = requests.get(urlPages, headers=headersIm).content   #requsets解析文章列表
    jsDict = json.loads(jsContent)        #将内容用json库解析
    jsResult = jsDict['result']
    jsData = jsResult['data']
    for each in jsData:
        newsUrl = each['url']   #获得相应文章的url
        getNewsContent(newsUrl) #函数解析文章内页内容

for i in range(1, 3):
    url_json = 'https://interface.sina.cn/pc_zt_api/pc_zt_press_news_doc.d.json?subjectID=64558&cat=&size=40&page='+str(i)+'&channel=sports'
        #构造json数据源的地址
    newsListPages(url_json)

csvfile.close()

六、总结:

request负责获取html页面,json负责解析数据源,bs负责解析html内页获取相关内容(中间涉及编码问题,在*找到答案),最后是写入txt(自带函数),写入csv(csv和codecs模块)。