Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 亚洲欧美日韩精品,在线不卡视频,亚洲精品日韩精品一区

          整合營銷服務商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          Spring 獲取 request 的幾種方法及其線程安全性分析

          使用Spring MVC開發(fā)Web系統(tǒng)時,經(jīng)常需要在處理請求時使用request對象,比如獲取客戶端IP地址、請求的URL、header中的屬性(如cookie、授權(quán)信息)、body中的數(shù)據(jù)等。由于在Spring MVC中,處理請求的Controller、Service等對象都是單例的,因此獲取request對象時最需要注意的問題,便是request對象是否是線程安全的:當有大量并發(fā)請求時,能否保證不同請求/線程中使用不同的request對象。

          這里還有一個問題需要注意:前面所說的“在處理請求時”使用request對象,究竟是在哪里使用呢?考慮到獲取request對象的方法有微小的不同,大體可以分為兩類:

          1、在Spring的Bean中使用request對象:既包括Controller、Service、Repository等MVC的Bean,也包括了Component等普通的Spring Bean。為了方便說明,后文中Spring中的Bean一律簡稱為Bean。

          2、在非Bean中使用request對象:如普通的Java對象的方法中使用,或在類的靜態(tài)方法中使用。

          此外,本文討論是圍繞代表請求的request對象展開的,但所用方法同樣適用于response對象、InputStream/Reader、OutputStream/ Writer等;其中InputStream/Reader可以讀取請求中的數(shù)據(jù),OutputStream/Writer可以向響應寫入數(shù)據(jù)。

          最后,獲取request對象的方法與Spring及MVC的版本也有關(guān)系;本文基于Spring4進行討論,且所做的實驗都是使用4.1.1版本。

          二、如何測試線程安全性

          既然request對象的線程安全問題需要特別關(guān)注,為了便于后面的討論,下面先說明如何測試request對象是否是線程安全的。

          測試的基本思路,是模擬客戶端大量并發(fā)請求,然后在服務器判斷這些請求是否使用了相同的request對象。

          判斷request對象是否相同,最直觀的方式是打印出request對象的地址,如果相同則說明使用了相同的對象。然而,在幾乎所有web服務器的實現(xiàn)中,都使用了線程池,這樣就導致先后到達的兩個請求,可能由同一個線程處理:在前一個請求處理完成后,線程池收回該線程,并將該線程重新分配給了后面的請求。而在同一線程中,使用的request對象很可能是同一個(地址相同,屬性不同)。因此即便是對于線程安全的方法,不同的請求使用的request對象地址也可能相同。

          為了避免這個問題,一種方法是在請求處理過程中使線程休眠幾秒,這樣可以讓每個線程工作的時間足夠長,從而避免同一個線程分配給不同的請求;另一種方法,是使用request的其他屬性(如參數(shù)、header、body等)作為request是否線程安全的依據(jù),因為即便不同的請求先后使用了同一個線程(request對象地址也相同),只要使用不同的屬性分別構(gòu)造了兩次request對象,那么request對象的使用就是線程安全的。本文使用第二種方法進行測試。

          客戶端測試代碼如下(創(chuàng)建1000個線程分別發(fā)送請求):

          服務器中Controller代碼如下(暫時省略了獲取Request對象的代碼):

          如果request對象線程安全,服務器中打印結(jié)果如下所示:

          如果存在線程安全問題,服務器中打印結(jié)果可能如下所示:

          如無特殊說明,本文后面的代碼中將省略掉測試代碼。

          三、方法1:Controller中加參數(shù)

          1、代碼示例

          這種方法實現(xiàn)最簡單,直接上Controller代碼:

          該方法實現(xiàn)的原理是,在Controller方法開始處理請求時,Spring會將request對象賦值到方法參數(shù)中。除了request對象,可以通過這種方法獲取的參數(shù)還有很多,具體可以參見:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

          Controller中獲取request對象后,如果要在其他方法中(如service方法、工具類方法等)使用request對象,需要在調(diào)用這些方法時將request對象作為參數(shù)傳入。

          2、線程安全性

          測試結(jié)果:線程安全

          分析:此時request對象是方法參數(shù),相當于局部變量,毫無疑問是線程安全的。線程安全的 Map 可以點此查看這篇文章。

          3、優(yōu)缺點

          這種方法的主要缺點是request對象寫起來冗余太多,主要體現(xiàn)在兩點:

          (1)如果多個controller方法中都需要request對象,那么在每個方法中都需要添加一遍request參數(shù)

          (2) request對象的獲取只能從controller開始,如果使用request對象的地方在函數(shù)調(diào)用層級比較深的地方,那么整個調(diào)用鏈上的所有方法都需要添加request參數(shù)

          實際上,在整個請求處理的過程中,request對象是貫穿始終的;也就是說,除了定時器等特殊情況,request對象相當于線程內(nèi)部的一個全局變量。而該方法,相當于將這個全局變量,傳來傳去.

          者:編程迷思https://www.cnblogs.com/kismetv/p/8757260.htmlJava架構(gòu)師做了編排


          概述

          在使用Spring MVC開發(fā)Web系統(tǒng)時,經(jīng)常需要在處理請求時使用request對象,比如獲取客戶端ip地址、請求的url、header中的屬性(如cookie、授權(quán)信息)、body中的數(shù)據(jù)等。由于在Spring MVC中,處理請求的Controller、Service等對象都是單例的,因此獲取request對象時最需要注意的問題,便是request對象是否是線程安全的:當有大量并發(fā)請求時,能否保證不同請求/線程中使用不同的request對象。

          這里還有一個問題需要注意:前面所說的“在處理請求時”使用request對象,究竟是在哪里使用呢?考慮到獲取request對象的方法有微小的不同,大體可以分為兩類:

          1) 在Spring的Bean中使用request對象:既包括Controller、Service、Repository等MVC的Bean,也包括了Component等普通的Spring Bean。為了方便說明,后文中Spring中的Bean一律簡稱為Bean。

          2) 在非Bean中使用request對象:如普通的Java對象的方法中使用,或在類的靜態(tài)方法中使用。

          此外,本文討論是圍繞代表請求的request對象展開的,但所用方法同樣適用于response對象、InputStream/Reader、OutputStream/ Writer等;其中InputStream/Reader可以讀取請求中的數(shù)據(jù),OutputStream/ Writer可以向響應寫入數(shù)據(jù)。

          最后,獲取request對象的方法與Spring及MVC的版本也有關(guān)系;本文基于Spring4進行討論,且所做的實驗都是使用4.1.1版本。

          如何測試線程安全

          既然request對象的線程安全問題需要特別關(guān)注,為了便于后面的討論,下面先說明如何測試request對象是否是線程安全的。

          測試的基本思路,是模擬客戶端大量并發(fā)請求,然后在服務器判斷這些請求是否使用了相同的request對象。

          判斷request對象是否相同,最直觀的方式是打印出request對象的地址,如果相同則說明使用了相同的對象。然而,在幾乎所有web服務器的實現(xiàn)中,都使用了線程池,這樣就導致先后到達的兩個請求,可能由同一個線程處理:在前一個請求處理完成后,線程池收回該線程,并將該線程重新分配給了后面的請求。而在同一線程中,使用的request對象很可能是同一個(地址相同,屬性不同)。因此即便是對于線程安全的方法,不同的請求使用的request對象地址也可能相同。

          為了避免這個問題,一種方法是在請求處理過程中使線程休眠幾秒,這樣可以讓每個線程工作的時間足夠長,從而避免同一個線程分配給不同的請求;另一種方法,是使用request的其他屬性(如參數(shù)、header、body等)作為request是否線程安全的依據(jù),因為即便不同的請求先后使用了同一個線程(request對象地址也相同),只要使用不同的屬性分別構(gòu)造了兩次request對象,那么request對象的使用就是線程安全的。本文使用第二種方法進行測試。

          客戶端測試代碼如下(創(chuàng)建1000個線程分別發(fā)送請求):

          public class Test {
           public static void main(String[] args) throws Exception {
           String prefix = UUID.randomUUID().toString().replaceAll("-", "") + "::";
           for (int i = 0; i < 1000; i++) {
           final String value = prefix + i;
           new Thread() {
           @Override
           public void run() {
           try {
           CloseableHttpClient httpClient = HttpClients.createDefault();
           HttpGet httpGet = new HttpGet("http://localhost:8080/test?key=" + value);
           httpClient.execute(httpGet);
           httpClient.close();
           } catch (IOException e) {
           e.printStackTrace();
           }
           }
           }.start();
           }
           }
          }
          


          服務器中Controller代碼如下(暫時省略了獲取request對象的代碼):

          @Controller
          public class TestController {
           // 存儲已有參數(shù),用于判斷參數(shù)是否重復,從而判斷線程是否安全
           public static Set<String> set = new ConcurrentSkipListSet<>();
           @RequestMapping("/test")
           public void test() throws InterruptedException {
           // …………………………通過某種方式獲得了request對象………………………………
           // 判斷線程安全
           String value = request.getParameter("key");
           if (set.contains(value)) {
           System.out.println(value + "\t重復出現(xiàn),request并發(fā)不安全!");
           } else {
           System.out.println(value);
           set.add(value);
           }
           // 模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          


          補充:上述代碼原使用HashSet來判斷value是否重復,經(jīng)網(wǎng)友批評指正,使用線程不安全的集合類驗證線程安全性是欠妥的,現(xiàn)已改為ConcurrentSkipListSet。

          如果request對象線程安全,服務器中打印結(jié)果如下所示:



          如果存在線程安全問題,服務器中打印結(jié)果可能如下所示:



          如無特殊說明,本文后面的代碼中將省略掉測試代碼。

          方法1:Controller中加參數(shù)

          代碼示例:

          這種方法實現(xiàn)最簡單,直接上Controller代碼:

          @Controller
          public class TestController {
           @RequestMapping("/test")
           public void test(HttpServletRequest request) throws InterruptedException {
           // 模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          


          該方法實現(xiàn)的原理是,在Controller方法開始處理請求時,Spring會將request對象賦值到方法參數(shù)中。除了request對象,可以通過這種方法獲取的參數(shù)還有很多,具體可以參見:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

          Controller中獲取request對象后,如果要在其他方法中(如service方法、工具類方法等)使用request對象,需要在調(diào)用這些方法時將request對象作為參數(shù)傳入。

          線程安全:

          測試結(jié)果:線程安全

          分析:此時request對象是方法參數(shù),相當于局部變量,毫無疑問是線程安全的。

          優(yōu)缺點:

          這種方法的主要缺點是request對象寫起來冗余太多,主要體現(xiàn)在兩點:

          1) 如果多個controller方法中都需要request對象,那么在每個方法中都需要添加一遍request參數(shù)

          2) request對象的獲取只能從controller開始,如果使用request對象的地方在函數(shù)調(diào)用層級比較深的地方,那么整個調(diào)用鏈上的所有方法都需要添加request參數(shù)

          實際上,在整個請求處理的過程中,request對象是貫穿始終的;也就是說,除了定時器等特殊情況,request對象相當于線程內(nèi)部的一個全局變量。而該方法,相當于將這個全局變量,傳來傳去。

          方法2:自動注入

          代碼示例:

          先上代碼:

          @Controller
          public class TestController{
           @Autowired
           private HttpServletRequest request; //自動注入request
           @RequestMapping("/test")
           public void test() throws InterruptedException{
           //模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          


          線程安全:

          測試結(jié)果:線程安全

          分析:在Spring中,Controller的scope是singleton(單例),也就是說在整個web系統(tǒng)中,只有一個TestController;但是其中注入的request卻是線程安全的,原因在于:

          使用這種方式,當Bean(本例的TestController)初始化時,Spring并沒有注入一個request對象,而是注入了一個代理(proxy);當Bean中需要使用request對象時,通過該代理獲取request對象。

          下面通過具體的代碼對這一實現(xiàn)進行說明。

          在上述代碼中加入斷點,查看request對象的屬性,如下圖所示:


          在圖中可以看出,request實際上是一個代理:代理的實現(xiàn)參見AutowireUtils的內(nèi)部類ObjectFactoryDelegatingInvocationHandler:

          /**
           * Reflective InvocationHandler for lazy access to the current target object.
           */
          @SuppressWarnings("serial")
          private static class ObjectFactoryDelegatingInvocationHandler implements InvocationHandler, Serializable {
           private final ObjectFactory<?> objectFactory;
           public ObjectFactoryDelegatingInvocationHandler(ObjectFactory<?> objectFactory) {
           this.objectFactory = objectFactory;
           }
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
           // ……省略無關(guān)代碼
           try {
           return method.invoke(this.objectFactory.getObject(), args); // 代理實現(xiàn)核心代碼
           }
           catch (InvocationTargetException ex) {
           throw ex.getTargetException();
           }
           }
          }
          

          也就是說,當我們調(diào)用request的方法method時,實際上是調(diào)用了由objectFactory.getObject()生成的對象的method方法;objectFactory.getObject()生成的對象才是真正的request對象。

          繼續(xù)觀察上圖,發(fā)現(xiàn)objectFactory的類型為WebApplicationContextUtils的內(nèi)部類RequestObjectFactory;而RequestObjectFactory代碼如下:

          /**
           * Factory that exposes the current request object on demand.
           */
          @SuppressWarnings("serial")
          private static class RequestObjectFactory implements ObjectFactory<ServletRequest>, Serializable {
           @Override
           public ServletRequest getObject() {
           return currentRequestAttributes().getRequest();
           }
           @Override
           public String toString() {
           return "Current HttpServletRequest";
           }
          }
          


          其中,要獲得request對象需要先調(diào)用currentRequestAttributes()方法獲得RequestAttributes對象,該方法的實現(xiàn)如下:

          /**
           * Return the current RequestAttributes instance as ServletRequestAttributes.
           */
          private static ServletRequestAttributes currentRequestAttributes() {
           RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
           if (!(requestAttr instanceof ServletRequestAttributes)) {
           throw new IllegalStateException("Current request is not a servlet request");
           }
           return (ServletRequestAttributes) requestAttr;
          }
          


          生成RequestAttributes對象的核心代碼在類RequestContextHolder中,其中相關(guān)代碼如下(省略了該類中的無關(guān)代碼):

          public abstract class RequestContextHolder {
           public static RequestAttributes currentRequestAttributes() throws IllegalStateException {
           RequestAttributes attributes = getRequestAttributes();
           // 此處省略不相關(guān)邏輯…………
           return attributes;
           }
           public static RequestAttributes getRequestAttributes() {
           RequestAttributes attributes = requestAttributesHolder.get();
           if (attributes == null) {
           attributes = inheritableRequestAttributesHolder.get();
           }
           return attributes;
           }
           private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
           new NamedThreadLocal<RequestAttributes>("Request attributes");
           private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
           new NamedInheritableThreadLocal<RequestAttributes>("Request context");
          }
          


          通過這段代碼可以看出,生成的RequestAttributes對象是線程局部變量(ThreadLocal),因此request對象也是線程局部變量;這就保證了request對象的線程安全性。

          優(yōu)缺點:

          該方法的主要優(yōu)點:

          1) 注入不局限于Controller中:在方法1中,只能在Controller中加入request參數(shù)。而對于方法2,不僅可以在Controller中注入,還可以在任何Bean中注入,包括Service、Repository及普通的Bean。

          2) 注入的對象不限于request:除了注入request對象,該方法還可以注入其他scope為request或session的對象,如response對象、session對象等;并保證線程安全。

          3) 減少代碼冗余:只需要在需要request對象的Bean中注入request對象,便可以在該Bean的各個方法中使用,與方法1相比大大減少了代碼冗余。

          但是,該方法也會存在代碼冗余。考慮這樣的場景:web系統(tǒng)中有很多controller,每個controller中都會使用request對象(這種場景實際上非常頻繁),這時就需要寫很多次注入request的代碼;如果還需要注入response,代碼就更繁瑣了。下面說明自動注入方法的改進方法,并分析其線程安全性及優(yōu)缺點。

          方法3:基類中自動注入

          代碼示例:

          與方法2相比,將注入部分代碼放入到了基類中。

          基類代碼:

          public class BaseController {
           @Autowired
           protected HttpServletRequest request; 
          }
          

          Controller代碼如下;這里列舉了BaseController的兩個派生類,由于此時測試代碼會有所不同,因此服務端測試代碼沒有省略;客戶端也需要進行相應的修改(同時向2個url發(fā)送大量并發(fā)請求)。

          @Controller
          public class TestController extends BaseController {
           // 存儲已有參數(shù),用于判斷參數(shù)value是否重復,從而判斷線程是否安全
           public static Set<String> set = new ConcurrentSkipListSet<>();
           @RequestMapping("/test")
           public void test() throws InterruptedException {
           String value = request.getParameter("key");
           // 判斷線程安全
           if (set.contains(value)) {
           System.out.println(value + "\t重復出現(xiàn),request并發(fā)不安全!");
           } else {
           System.out.println(value);
           set.add(value);
           }
           // 模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          @Controller
          public class Test2Controller extends BaseController {
           @RequestMapping("/test2")
           public void test2() throws InterruptedException {
           String value = request.getParameter("key");
           // 判斷線程安全(與TestController使用一個set進行判斷)
           if (TestController.set.contains(value)) {
           System.out.println(value + "\t重復出現(xiàn),request并發(fā)不安全!");
           } else {
           System.out.println(value);
           TestController.set.add(value);
           }
           // 模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          


          線程安全:

          測試結(jié)果:線程安全

          分析:在理解了方法2的線程安全性的基礎(chǔ)上,很容易理解方法3是線程安全的:當創(chuàng)建不同的派生類對象時,基類中的域(這里是注入的request)在不同的派生類對象中會占據(jù)不同的內(nèi)存空間,也就是說將注入request的代碼放在基類中對線程安全性沒有任何影響;測試結(jié)果也證明了這一點。

          優(yōu)缺點:

          與方法2相比,避免了在不同的Controller中重復注入request;但是考慮到java只允許繼承一個基類,所以如果Controller需要繼承其他類時,該方法便不再好用。

          無論是方法2和方法3,都只能在Bean中注入request;如果其他方法(如工具類中static方法)需要使用request對象,則需要在調(diào)用這些方法時將request參數(shù)傳遞進去。下面介紹的方法4,則可以直接在諸如工具類中的static方法中使用request對象(當然在各種Bean中也可以使用)。

          方法4:手動調(diào)用

          代碼示例:

          @Controller
          public class TestController {
           @RequestMapping("/test")
           public void test() throws InterruptedException {
           HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
           // 模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          


          線程安全:

          測試結(jié)果:線程安全

          分析:該方法與方法2(自動注入)類似,只不過方法2中通過自動注入實現(xiàn),本方法通過手動方法調(diào)用實現(xiàn)。因此本方法也是線程安全的。

          優(yōu)缺點:

          優(yōu)點:可以在非Bean中直接獲取。

          缺點:如果使用的地方較多,代碼非常繁瑣;因此可以與其他方法配合使用。

          方法5:@ModelAttribute方法

          代碼示例:

          下面這種方法及其變種(變種:將request和bindRequest放在子類中)在網(wǎng)上經(jīng)常見到:

          @Controller
          public class TestController {
           private HttpServletRequest request;
           @ModelAttribute
           public void bindRequest(HttpServletRequest request) {
           this.request = request;
           }
           @RequestMapping("/test")
           public void test() throws InterruptedException {
           // 模擬程序執(zhí)行了一段時間
           Thread.sleep(1000);
           }
          }
          


          線程安全:

          測試結(jié)果:線程不安全

          分析:@ModelAttribute注解用在Controller中修飾方法時,其作用是Controller中的每個@RequestMapping方法執(zhí)行前,該方法都會執(zhí)行。因此在本例中,bindRequest()的作用是在test()執(zhí)行前為request對象賦值。雖然bindRequest()中的參數(shù)request本身是線程安全的,但由于TestController是單例的,request作為TestController的一個域,無法保證線程安全。

          總結(jié):

          綜上所述,Controller中加參數(shù)(方法1)、自動注入(方法2和方法3)、手動調(diào)用(方法4)都是線程安全的,都可以用來獲取request對象。如果系統(tǒng)中request對象使用較少,則使用哪種方式均可;如果使用較多,建議使用自動注入(方法2 和方法3)來減少代碼冗余。如果需要在非Bean中使用request對象,既可以在上層調(diào)用時通過參數(shù)傳入,也可以直接在方法中通過手動調(diào)用(方法4)獲得。

          此外,本文在討論獲取request對象的方法時,重點討論該方法的線程安全性、代碼的繁瑣程度等;在實際的開發(fā)過程中,還必須考慮所在項目的規(guī)范、代碼維護等問題(此處感謝網(wǎng)友的批評指正)。

          更多JAVA干貨內(nèi)容,轉(zhuǎn)發(fā)+關(guān)注。私信我“資料”即可。

          ttp請求中Form Data 和 Request Payload兩種參數(shù)的區(qū)別 ?

          Ajax Post請求中常用的兩種的形式:form data 和 request payload

          一、默認的表單方式請求 Form Data


          post請求的Content-Type為application/x-www-form-urlencoded(默認的),參數(shù)是在請求題中,即上面請求中的Form Data。

          Content-Type: application/x-www-form-urlencoded; charset=UTF-8

          代碼格式:

          data = {
            'i': '\u903B\u8F91\n',
            'from': 'AUTO',
            'to': 'AUTO',
            'smartresult': 'dict',
            'client': 'fanyideskweb',
            'salt': '15752746021826',
            'sign': 'c62688ce2eab6fd7a95cac50c3e88752',
            'ts': '1575274602182',
            'bv': '5bc00aa7005fda30bbc3c3735a53d97d',
            'doctype': 'json',
            'version': '2.1',
            'keyfrom': 'fanyi.web',
            'action': 'FY_BY_REALTlME'
          }
          
          復制代碼

          二、經(jīng)瀏覽器解析后的表單請求 Request Payload



          PS: 請求的Content-Type是application/json;charset=UTF-8,而請求表單的參數(shù)在Request Payload中。

          Content-Type: application/json (這里用的是json格式)

          代碼格式:

          
          payload = '{"operationName":"","query":"","variables":{"ownerId":"5c3f3c415188252b7d0ea40c","size":20,"after":""},"extensions":{"query":{"id":"b158d18c7ce74f0d6d85e73f21e17df6"}}}'
          
          復制代碼

          二者之間的區(qū)別 ?

          post請求,如果表單參數(shù)是在請求體中,也是以key1=value1&key2=value2的形式在請求體中。

          通過chrome的開發(fā)者工具可以看到,比如:

          fanyi.youdao.com/translate_o…

          1、如果一個請求的Content-Type設(shè)置為

          Content-Type: application/x-www-form-urlencoded; charset=UTF-8

          那么這個Post請求會被認為是Http Post表單請求,請求主體也將以一個標準的鍵值對和&的str形式出現(xiàn)。這種方式是HTML表單默認的設(shè)置,對現(xiàn)如今的網(wǎng)絡請求構(gòu)造是很常見的。

          2、Request payload形式的POST請求,網(wǎng)站為了方便閱讀,使用了Json這樣的數(shù)據(jù)格式,請求的方式為

          Content-Type: application/json 或者指定charset=UTF-8。

          - 實戰(zhàn)

          使用requests模塊post payload請求

          在抓取個人數(shù)據(jù)的時候發(fā)現(xiàn)get形式獲取不到數(shù)據(jù),通過分析網(wǎng)站結(jié)構(gòu)發(fā)現(xiàn)需要Post請求的json格式數(shù)據(jù);進而發(fā)現(xiàn)其使用的Post格式并不是Form Data 而是Request Payload



          第一步:先請求拿到數(shù)據(jù)再說

          import requests
          import json
          
          # 首頁地址
          url = "https://web-api.juejin.im/query"
          
          # 偽裝成瀏覽器
          headers = {
              'X-Legacy-Device-Id': '1574318487465',
              'Origin': 'https://juejin.im',
              'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36',
              'X-Legacy-Token': 'eyJhY2Nlc3NfdG9rZW4iOiJBNVNuRUNPb1Jad0doWm1wIiwicmVmcmVzaF90b2tlbiI6IkpuVkFoZFozdjNFdDZMOFMiLCJ0b2tlbl90eXBlIjoibWFjIiwiZXhwaXJlX2luIjoyNTkyMDAwfQ==',
              'Content-Type': 'application/json',
              'Referer': 'https://juejin.im/user/3650034335487975',
              'X-Legacy-Uid': '5dd631975188254e310b4cbb',
          }
          
          payload = '{"operationName":"","query":"","variables":{"ownerId":"5c3f3c415188252b7d0ea40c","size":20,"after":""},"extensions":{"query":{"id":"b158d18c7ce74f0d6d85e73f21e17df6"}}}'
          
          # 發(fā)起網(wǎng)絡請求,獲取到返回的html
          result = requests.post(url=url, headers=headers, data=payload).content.decode('utf-8')
          print(result)
          
          復制代碼

          這時候已經(jīng)可以拿到payload表單形式的json數(shù)據(jù)了,因為考慮到是json格式的數(shù)據(jù),不方便我們進行數(shù)據(jù)處理!接下來咱們先轉(zhuǎn)換一下格式!這里轉(zhuǎn)換成字典格式。

          result=json.loads(result)
          result_list=result['data']['ownActivityFeed']['items']['edges']
          print(result_list)
          
          復制代碼

          這個時候已經(jīng)成功的將數(shù)據(jù)格式進行轉(zhuǎn)換,之后并通過一直獲取鍵值對的形式拿到網(wǎng)站所包含的數(shù)據(jù);數(shù)據(jù)類型的格式為列表, 再次深入獲取

          for item in result_list:
              # # 用戶名
              node_list=item['node']
              user_targets_content=node_list['targets']
              for item_name_list in user_targets_content:
                  try:
                      user=item_name_list['user']
                      user_name=user['username']
                      user_content=item_name_list['content']
                  except:
                      continue
                  print('*' * 30, '\n', user_name, user_content, '\n', '*' * 30)
                  with open('lg_Tony.txt','a') as file:
                      file.write(user_name+'\t\t'+user_content+'\n\n')
                      
          復制代碼

          考慮到只是獲取簡單的界面內(nèi)容,所以這里只用了txt文件進行保存。

          最終顯示數(shù)據(jù)內(nèi)容



          在這個浮躁的時代;竟然還有人能堅持篇篇原創(chuàng);

          如果本文對你學習有所幫助-可以點贊+ 關(guān)注!將持續(xù)更新更多新的文章。

          支持原創(chuàng)。感謝!


          主站蜘蛛池模板: 亚洲AV无码一区东京热| 日本视频一区在线观看免费| 亚洲综合激情五月色一区| 亚洲Av无码国产一区二区| 亚洲福利精品一区二区三区| 欲色aV无码一区二区人妻 | 无码少妇一区二区三区| 日韩精品一区二区三区毛片| 亚洲欧洲精品一区二区三区| 国产精品一区二区三区高清在线| 亚洲一区二区影视| 日韩一区二区电影| 国产一区二区视频在线播放 | 日本一区二区在线播放| 精品人妻少妇一区二区三区| 日韩精品一区二区三区中文版| 亚洲视频免费一区| 久久精品国产一区二区三| 久久久99精品一区二区| 国产一区二区三区播放| 人妻视频一区二区三区免费| 亚洲国产美女福利直播秀一区二区| 福利一区二区三区视频在线观看| 一区二区国产在线播放| 精品视频一区二区| 最新中文字幕一区| 成人毛片无码一区二区| 国产精品亚洲一区二区麻豆| 日韩一区精品视频一区二区 | 中文字幕一区二区人妻性色| 精品国产免费一区二区三区| 精品一区二区三区电影| 国产伦精品一区二区三区四区 | 精品在线视频一区| 国产伦理一区二区三区| 波多野结衣电影区一区二区三区| 日韩免费视频一区| 精品理论片一区二区三区| 国产一区二区三区精品久久呦| 在线视频一区二区日韩国产| 精彩视频一区二区三区|