抓取ajax技术构成的博客站

前言

折腾了一下午,实在是初学,网上找了很多资料,把python的官网提供的库也检索了一边,还好搞定了。

需要的python库有


import urllib.request
import urllib.parse
import re
import MySQLdb
import json

urllib.request这个库不用说,爬取网页信息常用的;urllib.parse这个库,我还是新学的,按照官方的说法叫

该模块定义了一个标准接口,用于分解组件中的统一资源定位符(URL)字符串(寻址方案,网络位置,路径等),将组件组合回URL字符串,并将“相对URL”转换为指定了“基本URL”的绝对URL。

说白了就是补全网址的,通过服务器需回调,post

import re这个库见得不是一天两天了,正则表达式模块import MySQLdb,python对MySQL操作需要的库,我用的是第三方的库,不是标准库。

headers

爬取得需要,伪装成浏览器。


headers ={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
    'x-requested-with': 'XMLHttpRequest'
    }
#全局安装headers
opener = urllib.request.build_opener()
opener.addheaders = [headers]
urllib.request.install_opener(opener)

写入数据库


def push_db(a, b, c, d, e): #写入库中
db = MySQLdb.connect(
    host = '127.0.0.1',
    port = 3306,
    user = '*****',  #数据库用户名
    password = '******', #数据库密码
    db = 'blog',
    charset = 'utf8'
)
cur = db.cursor()
sql =  "insert into other_blog (id, build_date, name, url, description) values (%s, %s, %s, %s, %s)"
try:
    cur.execute(sql, (a, b, c, d, e)) 
    cur.close()
    db.commit()
except:
    db.rollback()
    print("再试一次!")
db.close()

主要操作

由于我使用F12=>network=>XHR发现,总共只有五个json包,于是for i in range(0, 5):同时查看了一段源码,便得到了还算满意的正则表达式:


pat_1 = '"id":(.*?),'
pat_2 = '"date":"(.*?)",'
pat_3 = '"name":"(.*?)",'
pat_4 = '"url":"(.*?)"}\S'
pat_5 = '"message":"(.*?)",'

下面是全文件提取,形成一个list


id = re.compile(pat_1, re.S).findall(data)
build_date = re.compile(pat_2, re.S).findall(data)
name = re.compile(pat_3, re.S).findall(data)
url = re.compile(pat_4, re.S).findall(data)
description = re.compile(pat_5, re.S).findall(data)

核心

post的主要操作:可以参考python官方提供的源码及解释


values = {
    'page': str(i+1)
}  
url = 'https://foreverblog.cn/api/blogs'
postdata = urllib.parse.urlencode(data=values).encode('utf8')
data = urllib.request.urlopen(url, data=postdata).read().decode

other


#python官方给的源码
import urllib.request
import urllib.parse
data = urllib.parse.urlencode({'spam': 1, 'eggs': 2, 'bacon': 0}) #这里可以由一个values代替这个字典
data = data.encode('ascii')
with urllib.request.urlopen("http://requestb.in/xrbl82xr", data) as f:
    print(f.read().decode('utf-8'))

代码


import urllib.request
import urllib.parse
import re
import MySQLdb
headers ={
    'User-Agent':'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36',
    'x-requested-with': 'XMLHttpRequest'
    }
#全局安装headers
opener = urllib.request.build_opener()
opener.addheaders = [headers]
urllib.request.install_opener(opener)

def push_db(a, b, c, d, e): #写入库中
    db = MySQLdb.connect(
        host = '127.0.0.1',
        port = 3306,
        user = '****',   #数据库用户名
        password = '******', #数据库密码
        db = 'blog',
        charset = 'utf8'
    )
    cur = db.cursor()
    sql =  "insert into other_blog (id, build_date, name, url, description) values (%s, %s, %s, %s, %s)"
    try:
        cur.execute(sql, (a, b, c, d, e)) 
        cur.close()
        db.commit()
    except:
        db.rollback()
        print("再试一次!")
    db.close()

for i in range(0, 5):
    values = {'page': str(i+1)}
    url = 'https://foreverblog.cn/api/blogs'
    postdata = urllib.parse.urlencode(values).encode('utf8')
    data = urllib.request.urlopen(url, data=postdata).read().decode('utf8')

    pat_1 = '"id":(.*?),'
    pat_2 = '"date":"(.*?)",'
    pat_3 = '"name":"(.*?)",'
    pat_4 = '"url":"(.*?)"}\S'
    pat_5 = '"message":"(.*?)",'

    id = re.compile(pat_1, re.S).findall(data)
    build_date = re.compile(pat_2, re.S).findall(data)
    name = re.compile(pat_3, re.S).findall(data)
    url = re.compile(pat_4, re.S).findall(data)
    description = re.compile(pat_5, re.S).findall(data)

    for j in range(0, len(id)):
        a = json.loads(json.dumps(id[j].encode('utf-8').decode('unicode_escape')))
        b = json.loads(json.dumps(build_date[j].encode('utf-8').decode('unicode_escape')))
        c = json.loads(json.dumps(name[j].encode('utf-8').decode('unicode_escape')))
        d = json.loads(json.dumps(url[j].encode('utf8').decode("unicode_escape").replace("\/", "/")))
        e = json.loads(json.dumps(description[j].encode('utf-8').decode('unicode_escape')))))
        push_db(int(a), b, c, d, e) #网站编辑器的原因,源码是对齐上一行的开头的
'''
    print(id)
    print(build_date)
    print(name)
    print(url)
    print(description)
    print("\n\n")
'''

后记

这个编码问题也是一个很重要的问题, request输出的是str数据类型 而该网站post的结果是以json文件的形式传输的,所有需要解码json的库import json,同时也要解码unicode,将其转换成中文。 还有这里parse = urllib.parse.urlencode(values).encode('utf8')犯了一个错误,不应当转换成bytes类型,没必要,多了格式转换步骤。

参考资料: 1.UnicodeEncodeError: 'utf-8' codec can't encode characters in position 10-11: surrogates not allowed 2.json.dumps()和json.loads()

补充

2020-5-14 其实不使用urlopen,改用Request更好,Request的拓展性更好。