如何在 C# HttpClient 中循环调用分页 URL 以从 JSON 结果中下载所有页面
Posted
技术标签:
【中文标题】如何在 C# HttpClient 中循环调用分页 URL 以从 JSON 结果中下载所有页面【英文标题】:How to Loop calls to Pagination URL in C# HttpClient to download all Pages from JSON results 【发布时间】:2017-04-17 05:24:33 【问题描述】:我的第一个问题,请善待... :)
我正在使用 C# HttpClient
来调用 Jobs API Endpoint。
这里是端点:Jobs API Endpoint (doesn't require key, you can click it)
这给了我这样的 JSON。
"count": 1117,
"firstDocument": 1,
"lastDocument": 50,
"nextUrl": "\/api\/rest\/jobsearch\/v1\/simple.json?areacode=&country=&state=&skill=ruby&city=&text=&ip=&diceid=&page=2",
"resultItemList": [
"detailUrl": "http:\/\/www.dice.com\/job\/result\/90887031\/918715?src=19",
"jobTitle": "Sr Security Engineer",
"company": "Accelon Inc",
"location": "San Francisco, CA",
"date": "2017-03-30"
,
"detailUrl": "http:\/\/www.dice.com\/job\/result\/cybercod\/BB7-13647094?src=19",
"jobTitle": "Platform Engineer - Ruby on Rails, AWS",
"company": "CyberCoders",
"location": "New York, NY",
"date": "2017-04-16"
]
我已经粘贴了一个完整的 JSON sn-p,所以你可以在你的答案中使用它。完整的结果在这里真的很长。
这里是 C# 类。
using Newtonsoft.Json;
using System.Collections.Generic;
namespace MyNameSpace
public class DiceApiJobWrapper
public int count get; set;
public int firstDocument get; set;
public int lastDocument get; set;
public string nextUrl get; set;
[JsonProperty("resultItemList")]
public List<DiceApiJob> DiceApiJobs get; set;
public class DiceApiJob
public string detailUrl get; set;
public string jobTitle get; set;
public string company get; set;
public string location get; set;
public string date get; set;
当我使用 HttpClient 调用 URL 并使用 JSON.NET 反序列化时,我确实可以正确取回数据。
这是我从控制台应用程序的Main
方法调用的代码(因此static
列表,我认为这可以更好地重构??)
private static List<DiceApiJob> GetDiceJobs()
HttpClient httpClient = new HttpClient();
var jobs = new List<DiceApiJob>();
var task = httpClient.GetAsync("http://service.dice.com/api/rest/jobsearch/v1/simple.json?skill=ruby")
.ContinueWith((taskwithresponse) =>
var response = taskwithresponse.Result;
var jsonString = response.Content.ReadAsStringAsync();
jsonString.Wait();
var result = JsonConvert.DeserializeObject<DiceApiJobWrapper>(jsonString.Result);
if (result != null)
if (result.DiceApiJobs.Any())
jobs = result.DiceApiJobs.ToList();
if (result.nextUrl != null)
//
// do this GetDiceJobs again in a loop? How?? Any other efficient elegant way??
);
task.Wait();
return jobs;
但是现在,我如何使用nextUrl
字段检查是否还有更多工作?我知道我可以检查它是否不为空,如果不为空,则意味着还有更多工作需要下拉。
Results from my debugging and stepping through
如何以递归方式执行此操作,并且不会挂起并且有一些延迟,这样我就不会超过 API 限制?我想我必须使用 TPL(任务并行库),但我很困惑。
谢谢! ~肖恩
【问题讨论】:
在有人推荐递归路由之前,不要这样做。如果您有足够的页面,您将获得堆栈溢出。我会把它放在一个循环中(while (nextUrl != null)
),然后在 while 循环之前将 nextUrl 分配给第一个,等等。
难道你不能只有一个队列列表,然后循环它并在迭代时添加/弹出。还需要跟踪您查看过的网址,以避免抓取已报废的页面。
@john 谢谢,我同意这很糟糕,我将标题改为Loop
。 @Phill 我听说过 QueueList,你能详细说明一下,或者在 Answer 中提供一个工作代码。我对 TPL 和异步任务库等非常陌生。
@SeanPatel 您正在使用 DeserializeObject 将其映射到 c# 类。如果 json 中缺少任何内容,它将破坏您的代码。
@AnirudhaGupta 哦。那么推荐的方法是什么?抱歉,对这一切都很陌生。是否有不同的更安全的“防御性编码”方法?
【参考方案1】:
如果您担心应用的响应时间,并希望在实际从 API 获取所有页面/数据之前返回一些结果,您可以在循环中运行您的流程,并为其提供一个回调方法来执行它从 API 获取每一页数据。
这是一个示例:
public class Program
public static void Main(string[] args)
var jobs = GetDiceJobsAsync(Program.ResultCallBack).Result;
Console.WriteLine($"\nAll jobs.Count jobs displayed");
Console.ReadLine();
private static async Task<List<DiceApiJob>> GetDiceJobsAsync(Action<DiceApiJobWrapper> callBack = null)
var jobs = new List<DiceApiJob>();
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://service.dice.com");
var nextUrl = "/api/rest/jobsearch/v1/simple.json?skill=ruby";
do
await httpClient.GetAsync(nextUrl)
.ContinueWith(async (jobSearchTask) =>
var response = await jobSearchTask;
if (response.IsSuccessStatusCode)
string jsonString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<DiceApiJobWrapper>(jsonString);
if (result != null)
// Build the full list to return later after the loop.
if (result.DiceApiJobs.Any())
jobs.AddRange(result.DiceApiJobs.ToList());
// Run the callback method, passing the current page of data from the API.
if (callBack != null)
callBack(result);
// Get the URL for the next page
nextUrl = (result.nextUrl != null) ? result.nextUrl : string.Empty;
else
// End loop if we get an error response.
nextUrl = string.Empty;
);
while (!string.IsNullOrEmpty(nextUrl));
return jobs;
private static void ResultCallBack(DiceApiJobWrapper jobSearchResult)
if (jobSearchResult != null && jobSearchResult.count > 0)
Console.WriteLine($"\nDisplaying jobs jobSearchResult.firstDocument to jobSearchResult.lastDocument");
foreach (var job in jobSearchResult.DiceApiJobs)
Console.WriteLine(job.jobTitle);
Console.WriteLine(job.company);
请注意,上述示例允许回调方法访问GetDiceJobsAsync
方法接收到的每一页数据。在这种情况下,控制台会在每个页面可用时显示它。如果您不想要回调选项,您可以简单地不向GetDiceJobsAsync
传递任何内容。
但GetDiceJobsAsync
在完成时也会返回所有作业。所以你可以选择对GetDiceJobsAsync
末尾的整个列表进行操作。
至于达到 API 限制,您可以在循环中插入一小段延迟,就在您重复循环之前。但是当我尝试它时,我没有遇到限制我请求的 API,所以我没有将它包含在示例中。
【讨论】:
这很漂亮。工作,真的很快!谢谢弗兰克!我接受了答案,并为你投票,但由于我是新人,它说我的投票很重要,但没有出现或其他什么。希望你能投票。非常感谢!!!以上是关于如何在 C# HttpClient 中循环调用分页 URL 以从 JSON 结果中下载所有页面的主要内容,如果未能解决你的问题,请参考以下文章
如何使用httpclient c#调用bitly v4 api来缩短url?