python-爬虫之批量爬取微博指定用户高清图片

时间:2024-03-08 16:23:13

获取cookie

  用浏览器登录微博:新浪微博

  注意事项:在登录之前先按F12,确保跳出以下界面,试过很多次找不到cookie就是因为没先打开这个:

  

  然后登录微博,找到自己的cookie,把cookie保存下来后面用来访问微博,接下来就可以进入正题了。

  

1.导入模块

import requests                #访问微博网站以及数据获取
import re                      #解析网页数据
import pyquery as pq           #解析html
import json                    #反序列化
import time            

2.初始化全局变量

为了省事这里采用了简单粗暴的方法,直接将这些复制在代码里了,显得有点丑。也可以更优雅一点,将这些写在json文件里,然后再读出来,会显得好看一些

headers:就是浏览器访问网站时会携带这些数据,网站服务器就可以识别了,有些网站如果没有请求头会出错,这个可以在刚刚获取cookie那里找到。

User-Agent:包含了浏览器的版本信息,也可以从这里知道是移动端还是PC端访问的

Cookie:相当于用户认证,登录之后网站会给你设置cookie,带着这个cookie访问网站就可以不用重新登录了。

COOKIE = ""        #双引号里放刚刚保存的cookie
HEADERS = {
    \'User-Agent\': \'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36\',\
    \'Cookie\': COOKIE
}                     #请求头
DECODE_DICT={
    \'%2F\':\'/\',
    \'mw1024\':\'large\'
}                     #这是用来解码微博图片url的
URLS=[]              #用来存放图片url

3.查找用户

 

1.当我们在微博页面查找用户时,可以看到url就是:https://s.weibo.com/user?q=用户名&Refer=weibo_user,所以通过这个url去访问即可获取查找完成后的页面

 

2.其实一般来说每次找人肯定是那个人在第一个,所以这里的for循环换成直接获取第一个标签的url应该也可以,但是这里还是写了个循环判断一下

3.pyquery用法跟jQuery一样,可以方便地找出满足条件的标签,通过“.card .info .name”即可找到对应的标签,通过tag.text()获取名字,并判断是否正确

def query_user():
    name=input(\'>>:\').strip()      
    text = req.get(\'https://s.weibo.com/user?q=%s&Refer=weibo_user\'%name, headers=HEADERS).text                 #查找目标用户
    time.sleep(1)                              
    html=pq.PyQuery(text)                                    #将查找后获得的html转成pyquery对象
    for tag in html(".card .info .name"):                            #获取所有带有用户名的标签
        tag = pq.PyQuery(tag)
        if tag.text()==name:                                   
            link=tag.attr(\'href\')
            link=\'https:\'+link
            response = req.get(link.format(name), headers=HEADERS)    #找到标签里用户名与目标用户名一致的标签,获取url,访问该用户
            text=response.text
            enter_album(text)                         #进入用户的相册
            time.sleep(1)
            download_pic()                            #开始下载图片
       break

4.进入相册

1.当时为了尽快实现功能所以这个函数写得有点乱

2.在上面查找到用户的时候调用了enter_album(html)函数,这个函数就是进入用户相册之后进行处理。

  • 进入相册之后并没有任何图片,所以要通过这个相册的页面,获取相册第一页的url
  • 用户相册除了第一页,其他页都是动态渲染的,所以在访问前一页的图片时还要获得下一页图片的相关信息,然后通过这个信息访问下一页的图片。

3.这里用到正则表达式是因为这个相册的界面是动态渲染的,所以得到的数据并不是html的格式,所以先通过re模块获取数据然后拿到html,

因为懒得写太复杂的表达式,所以只是粗略过滤一下,所以data里的数据并不全是json格式,如果不用try直接就报错退出了。。。真是偷懒到极致了我。。

4.拿到html后就可以跟上面的方法一样找到相应的标签,获取url,这里直接找了“tab_link”,因为“她的主页”,"她的相册","作品"都用到了“tab_link”样式,所以加了判断

判断获取的href里是否有photos字符串,通过这个url访问相册。

 

5.进入相册后找到运用了“.WB_cardwrap”样式的标签,获取action-data属性,对这个字符串进行处理获取相册下一页的信息

 

 

6.获取图片url:找到\'运用了“ph_ar_box”样式的标签,获取action-data属性,通过对字符串处理获取到划红线的子串(这一步在parse_url里实现),这个子串就是图片的url

def enter_album(html):
    item_dict = {}                                                  #这个字典用来存放下一页的相关信息
#获取相册第一页的相关信息
    data=re.findall(\'<script>FM.view\((.*?)\)\', html, re.S)         #用正则表达式获取数据
    url=None
    for item in data:
        try:
            item=json.loads(item)                                   #反序列化
            item=item.get(\'html\')                          
            html=pq.PyQuery(item)
            keyword=\'photos\'
            for tag in html(\'.tab_link\'):
                href=tag.get(\'href\')
                if keyword in href:
                    url=\'https://weibo.com\'+href
                    item_dict[\'page_id\']=re.search(\'p/(.\d+)/\',url).group()[2:-1]
                    break
        except Exception as e:
            continue
#获取第一页所有照片的url以及下一页的相关信息
        if url:
            response = req.get(url,headers=HEADERS)
            htmls=response.content
            data = re.findall(\'<script>FM.view\((.*?)\)</script>\', htmls.decode(\'utf-8\'), re.S)
            for html in data:
                try:
                    html = json.loads(html)
                    html = html.get(\'html\')
                    html = pq.PyQuery(html)
                    next_page=html(\'.WB_cardwrap\').attr(\'action-data\')
                    if next_page:
                        html(\'.ph_ar_box\').each(parse_url)         #获得此页所有含有图片url的标签,并将此标签传到parse_url函数
                        next_page = next_page.split(\'&\')
                        for sub in next_page:
                            sub = sub.split(\'=\')
                            item_dict[sub[0]] = sub[1]
                        get_url(item_dict)                #获取相册下一页的图片url
                except Exception as e:
                    continue
            break        

5.图片url解码

上面说到图片的url,还需要解码变成真正的url,其实就是将里面的一些字符替换了,百度图片的url也是这样子的。

def parse_url(index,tag):
    tmp_url=tag.get(\'action-data\').strip("curclear_picSrc=")    #获取未解码的图片url
    url=tmp_url[:tmp_url.find(\'.jpg\')+4]
    for key in DECODE_DICT:
        url=url.replace(key,DECODE_DICT[key])                   #用初始化时的解码字典进行替换,得到图片真正的url
    URLS.append("http:"+url)                                    #将图片url存到URLS列表中

6.获取相册下一页的图片url

当我们滚动页面查看下一页相册时会看到这个,这是前端给后台发的数据,后台根据这个数据返回数据给前端

 ajwvr:暂时不知道是什么,一直都是6

owner_uid:就是你访问的用户的id

viewer_uid:自己的id

since_id:应该是当页相册对应的第一条微博的id和最后一条微博的id,还有时间

page:就是相册的页码

ajax_call:可能是ajax回调函数的类型,根据这个参数调用相关的函数。。我猜的。。反正填1就对了

_rnd:时间戳

def get_url(data_dict):
    q_url = \'https://weibo.com/p/aj/album/loading\'
    user_data=data_dict
    for i in range(1,10000):                               #这里是从第一页获取到最后一页
        params= {
                    \'ajwvr\': 6,
                    \'type\': \'photo\',
                    \'owner_uid\': int(user_data[\'owner_uid\']),
                    \'viewer_uid\': int(user_data[\'viewer_uid\']),
                    \'since_id\': user_data[\'since_id\'],
                    \'page_id\': int(user_data[\'page_id\']),
                    \'page\': i,
                    \'ajax_call\': 1,
                    \'__rnd\': time.time(),
                }
        try:
            url = requests.get(q_url, headers=HEADERS, params=params) #刷新下一页
            url = url.json().get(\'data\')
            url = pq.PyQuery(url)
            print("page:%d" % i)
            time.sleep(1)
            url(\'.ph_ar_box\').each(parse_url)
            try:
                user_data[\'since_id\'] = url(\'.WB_cardwrap\').attr(\'action-data\')
                user_data[\'since_id\'] = user_data[\'since_id\'][user_data[\'since_id\'].rfind(\'since_id=\') + 9:]
            except Exception as e:                                    #最后一页则退出
                return None
        except Exception as e:
            print(e)
            continue

7.下载图片

 最后就是去访问URLS里的图片url并保存,这里保存路径写死了,可以优化一下自定义路径

def download_pic():
    global URLS
    for url in URLS:
        try:
            content = req.get(url, headers=HEADERS).content
        except Exception as e:
            continue
        filename = url[url.rfind("/") + 1:]
        with open("test/%s" %  filename, \'wb\') as file:
            file.write(content)
        time.sleep(1)
    else:
        URLS = []

8.开始下载

这里可以加个循环去调用query_user(),这样下载完就不会直接退出了

if __name__=="__main__":
    req=requests.session()        #创建会话
    query_user()                  #调用函数查找用户