Python爬虫练习:爬取csdn极客的更新文章

写在前面:这两周花了点时间读了《Python网络数据采集》,内容不多,不到200页,但是非常丰富,有入门,有提高,有注意事项,有经验之谈,有原理,有分析,读完受益匪浅。书中讲了很多反爬虫、图片验证码之类的东西,不过感谢csdn的开放性,这些都没有。所以第一个练习,就是爬取csdn的极客头条的更新文章。

1  思路

思路比较简单,首先是登录,然后爬取页面的更新文章名称和链接。要注意的一点是,极客头条的列表刷新是动态的,只有页面有滚动条并且往下拉的时候,才会加载新的文章列表。我用竖屏显示器试了下,没有滚动条的情况下,默认显示20条的文章列表,结果不能加载新的文章列表,应该算是bug。

2  准备

通过浏览器的开发人员工具抓包,可以发现极客头条申请新列表的时候URL格式如下:

http://geek.csdn.net/service/news/get_news_list?jsonpcallback=jQuery203014439105321047596_1516862462757&username=[账户名]&from=-&size=20&type=hackernewsv2_new&_=1516862462758

请求参数:

  1. jsonpcallback:
    jQuery20302827217349787545_1516863701413 #该参数是jQuery框架自动生成的匿名回调函数的函数名,用于ajax获取数据时的数据处理,看网页源代码,应该是利用getJSON,所以是页面端生成的参数,可以随意填写
  2. username: [账户名]
  3. from:
    6:252765 #这个参数代表的是下一次请求文章列表时,文章的起始编号,如果是第一次请求列表,则这里填‘-’(短杠),和上面例子中一样,下次编号会在本次请求返回的JSON数据中携带
  4. size:
    20 #本次请求的文章条目数,我试过1000都成功了。。。
  5. type:
    hackernewsv2_new #文章类型,类型在首页的“最热 最新 业界”等等那一行小标题,选择的分类不同,这个参数不同,具体抓包可见
  6. _:
    1516863701415 #没什么用,就是第一个参数下短杠后面的数字累加,实际测试没有也可以
通过查找资料和抓包,发现csdn的登录还是很简单的,只要用户名密码,不需要验证码等等,抓包可以看到请求参数:

  1. gps:
    39.890503,116.431339
  2. username:
    [账户名]
  3. password:
    [密码] #抓包的话这里是明码,发出去的话应该是加密的
  4. rememberMe:
    true #是否记住密码
  5. lt:
    LT-448149-vgNusKFi3i7wBRIZUrzCFLDfoDVP34 #这个参数是在登录主页面中的,需要自己解析出来,数值随机,每次登录需要获取
  6. execution:
    e3s1 #目前是固定值,和网文对比这个值不同,所以还是每次登录获取的好
  7. _eventId:
    submit #固定值,就是代表提交
登录时要注意的是,csdn为了防爬虫,要求HTTP头的User-Agent字段必须是真实的,所以我用了抓包里面真实的浏览器填充的字段,否则会一直登录失败,返回登录页。

通过抓包可以看到,请求文章后,返回的是json数据,其中‘from’自动用于下次请求,‘html’字段就是返回的网页,utf-8编码的Unicode字符串,Python默认用的就是Unicode,所以取出html字段的数据后自动转为了汉字、符号等,然后解析其中的class类型为‘title’的链接,就可以获得文章链接和名称。

3  代码(非常短)

"""
请求geek头条的文章列表
1 登录csdn
2 打开极客头条
3 请求头条文件列表和链接
4 解析出文章名称和链接
"""
import requests
from bs4 import BeautifulSoup
import time
import json


def login_csdn(session):
    """
    登录CSDN
    :return:TRUE 成功 FALSE 识别
    """
    page_login = session.get('https://passport.csdn.net/account/login')
    bsObj = BeautifulSoup(page_login.content, 'lxml')
    eventID = bsObj.find('input', {'name': '_eventID'})
    lt = bsObj.find('input', {'name': 'lt'})
    execution = bsObj.find('input', {'name': 'execution'})
    params = { 'eventID': eventID, 'lt': lt, 'execution': execution,
               'username': '', 'password': '', }
    headers = {'User-Agent': r'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0'}
    time.sleep(1)
    page_login_succ = session.post('https://passport.csdn.net/account/verify', params=params, headers=headers)
    if page_login_succ.status_code == 200:
        print('login csdn success ~~~')
        return True
    else:
        print('login csdn failed: ' + page_login_succ.status_code)
        return False

def open_geek(session):
    """
    打开极客头条
    :param session:
    :return:
    """
    page_geek = session.get('http://geek.csdn.net/?ref=toolbar_logo')
    if page_geek.status_code == 200:
        print('geek success ~~~')
        #http://geek.csdn.net/service/news/get_news_list?jsonpcallback=jQuery20306392256918709227_1516849580010&username=&from=-&size=20&type=HackCount&_=1516849580011
        #其中,jsoncallback是jQuery自动生成的lamda函数的名称,所以可以任意填,from=-,代表从最开始发送,size=20,代表返回20个结果
        #当返回以后,结果中会带有下一页数据的起始码,用于from,type不变,参数'_'后面是随机数字,可以不填
        articls_url = 'http://geek.csdn.net/service/news/get_news_list?' \
                      'jsonpcallback=jQuery21305830119664065718_1516862161253' \
                      '&username={}&from={}&size=20&type=hackernewsv2_new&_=1516862161253'
        page_geek_pageone = session.get(articls_url.format('', '-'))
        if page_geek_pageone.status_code == 200:
            jsonObj = get_next_articls_urls(page_geek_pageone)
            page_geek_pagenext = session.get(articls_url.format('', jsonObj['from']))
            print('\n----------------------------------------\n')
            get_next_articls_urls(page_geek_pagenext)
    else:
        print('geek failed: ' + page_geek.status_code)


def get_next_articls_urls(geek_page):
    """
    辅助函数,解析返回的json数据中的文章信息
    :param geek_page:
    :return:
    """
    data = geek_page.text[geek_page.text.find('(') + 1: len(geek_page.text) - 1]
    jsonObj = json.loads(data)
    # print(jsonObj['html'])
    bsObj = BeautifulSoup(jsonObj['html'], 'lxml')
    urls = bsObj.findAll('a', {'class': 'title'})
    for url in urls:
        print(url)
    return jsonObj


if __name__ == '__main__':
    session = requests.session()
    if login_csdn(session):#登录成功
        open_geek(session)




猜你喜欢

转载自blog.csdn.net/blwinner/article/details/79161907