Skip to content

Commit 0f6896d

Browse files
committed
Add project files.
1 parent 7332f62 commit 0f6896d

6 files changed

+232
-0
lines changed

AddIn.cs

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using ExcelDna.Integration;
2+
using ExcelDna.IntelliSense;
3+
using ExcelDna.Registration;
4+
5+
namespace ImportFunctions
6+
{
7+
public class AddIn : IExcelAddIn
8+
{
9+
public void AutoOpen()
10+
{
11+
ExcelRegistration.GetExcelFunctions()
12+
.ProcessAsyncRegistrations()
13+
.RegisterFunctions();
14+
15+
IntelliSenseServer.Install();
16+
}
17+
18+
public void AutoClose()
19+
{
20+
IntelliSenseServer.Uninstall();
21+
}
22+
}
23+
}

Functions.cs

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
using System;
2+
using System.Net;
3+
using System.Net.Http;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using System.Xml;
7+
using ExcelDna.Integration;
8+
using ExcelDna.Registration;
9+
using HtmlAgilityPack;
10+
11+
namespace ImportFunctions
12+
{
13+
public static class Functions
14+
{
15+
// We will be using the single HttpClient from multiple threads,
16+
// which is OK as long as we're not changing the default request headers.
17+
static readonly HttpClient _httpClient;
18+
19+
static Functions()
20+
{
21+
_httpClient = new HttpClient();
22+
ServicePointManager.SecurityProtocol =
23+
SecurityProtocolType.Tls |
24+
SecurityProtocolType.Tls11 |
25+
SecurityProtocolType.Tls12 |
26+
SecurityProtocolType.Tls13;
27+
}
28+
29+
[ExcelAsyncFunction(Description = "Imports data from a given URL using an XPath query")]
30+
public static async Task<object> ImportXml(string url, string xpathQuery)
31+
{
32+
if (string.IsNullOrWhiteSpace(url))
33+
{
34+
return "Error: URL is required";
35+
// return ExcelError.ExcelErrorValue;
36+
}
37+
38+
if (string.IsNullOrWhiteSpace(xpathQuery))
39+
{
40+
return "Error: XPath query is required";
41+
// return ExcelError.ExcelErrorValue;
42+
}
43+
44+
try
45+
{
46+
var response = await _httpClient.GetStringAsync(url);
47+
var doc = new HtmlDocument();
48+
doc.LoadHtml(response);
49+
50+
var node = doc.DocumentNode.SelectSingleNode(xpathQuery);
51+
return node?.InnerText ?? "Error: No data found for the given XPath query";
52+
}
53+
catch (HttpRequestException rex)
54+
{
55+
return $"Error: Unable to fetch data from the URL - {rex.Message}";
56+
}
57+
catch (XmlException xex)
58+
{
59+
return $"Error: Invalid XML data - {xex.Message}";
60+
}
61+
catch (Exception ex)
62+
{
63+
return $"Error: {ex.Message}";
64+
}
65+
}
66+
67+
[ExcelFunction(Description = "Imports data from a table or list within an HTML page")]
68+
public static async Task<object> ImportHtml(
69+
[ExcelArgument(Description = "URL of the HTML page to scrape data from. The URL must start with either http or https.")]
70+
string url,
71+
[ExcelArgument(Description = "Type of data to import. Accepts either 'table' for HTML tables or 'list' for HTML lists (ul/ol).")]
72+
string dataType,
73+
[ExcelArgument(Description = "Zero-based index of the table or list to import from the HTML page. For example, 0 for the first table/list, 1 for the second, and so on.")]
74+
int index)
75+
{
76+
if (string.IsNullOrWhiteSpace(url))
77+
{
78+
return "Error: URL is required";
79+
// return ExcelError.ExcelErrorValue;
80+
}
81+
82+
if (dataType != "table" && dataType != "list")
83+
{
84+
return "Error: Data type must be 'table' or 'list'";
85+
// return ExcelError.ExcelErrorValue;
86+
}
87+
88+
try
89+
{
90+
var response = await _httpClient.GetStringAsync(url);
91+
var doc = new HtmlDocument();
92+
doc.LoadHtml(response);
93+
94+
if (dataType == "table")
95+
return ExtractTable(doc, index);
96+
else
97+
return ExtractList(doc, index);
98+
}
99+
catch (HttpRequestException rex)
100+
{
101+
return $"Error: Unable to fetch data from the URL - {rex.Message}";
102+
}
103+
catch (Exception ex)
104+
{
105+
return $"Error: {ex.Message}";
106+
}
107+
}
108+
109+
static object ExtractTable(HtmlDocument doc, int index)
110+
{
111+
var tables = doc.DocumentNode.SelectNodes("//table");
112+
if (tables == null || tables.Count <= index)
113+
return "Error: Table not found";
114+
115+
var table = tables[index];
116+
var sb = new StringBuilder();
117+
118+
foreach (var row in table.SelectNodes("tr"))
119+
{
120+
foreach (var cell in row.SelectNodes("th|td"))
121+
{
122+
sb.Append(cell.InnerText.Trim());
123+
sb.Append("\t"); // Tab-separated values
124+
}
125+
sb.AppendLine(); // New line at the end of each row
126+
}
127+
128+
return sb.ToString();
129+
}
130+
131+
static object ExtractList(HtmlDocument doc, int index)
132+
{
133+
var lists = doc.DocumentNode.SelectNodes("//ul | //ol");
134+
if (lists == null || lists.Count <= index)
135+
return "Error: List not found";
136+
137+
var list = lists[index];
138+
var sb = new StringBuilder();
139+
140+
foreach (var item in list.SelectNodes("li"))
141+
{
142+
sb.AppendLine(item.InnerText.Trim());
143+
}
144+
145+
return sb.ToString();
146+
}
147+
}
148+
}

ImportFunctions-Example.xlsx

10.6 KB
Binary file not shown.

ImportFunctions.csproj

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net48</TargetFramework>
5+
6+
<AssemblyTitle>Excel-DNA ImportFunctions Add-In</AssemblyTitle>
7+
<Product>ExcelDna.ImportFunctions</Product>
8+
9+
<ExcelAddInExplicitRegistration>true</ExcelAddInExplicitRegistration>
10+
11+
<ExcelDnaPackXllSuffix></ExcelDnaPackXllSuffix>
12+
<ExcelDnaPack32BitXllName>ExcelDna.ImportFunctions32</ExcelDnaPack32BitXllName>
13+
<ExcelDnaPack64BitXllName>ExcelDna.ImportFunctions64</ExcelDnaPack64BitXllName>
14+
</PropertyGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="ExcelDna.AddIn" Version="1.7.0" />
18+
<PackageReference Include="ExcelDna.IntelliSense" Version="1.7.0" />
19+
<PackageReference Include="ExcelDna.Registration" Version="1.7.0" />
20+
<PackageReference Include="HtmlAgilityPack" Version="1.11.55" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<Reference Include="System.Net.Http" />
25+
</ItemGroup>
26+
27+
</Project>

ImportFunctions.sln

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.8.34316.72
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ImportFunctions", "ImportFunctions.csproj", "{72EA066C-B79A-48CC-B418-696C790298D5}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{72EA066C-B79A-48CC-B418-696C790298D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{72EA066C-B79A-48CC-B418-696C790298D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{72EA066C-B79A-48CC-B418-696C790298D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{72EA066C-B79A-48CC-B418-696C790298D5}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {05982616-29F8-46D5-9E6B-090ACF9CA379}
24+
EndGlobalSection
25+
EndGlobal

Properties/launchSettings.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"profiles": {
3+
"Excel": {
4+
"commandName": "Executable",
5+
"executablePath": "C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE",
6+
"commandLineArgs": "/x \"ImportFunctions-AddIn64.xll\""
7+
}
8+
}
9+
}

0 commit comments

Comments
 (0)