在使用.NET Core的库csproj中使用Selenium WebDriver

8
我正在尝试使用Selenium和.NET Core构建一个网页爬取应用程序,但我在找到WebDriver的exe文件方面遇到了困难。
我的项目中有一个.csproj,用于运行API,其中调用(除其他外)另一个.csproj来处理网页爬取。所有都在一个.sln中运行,且都使用.NET Core 2.1。
在爬取项目中,我已经通过nuget安装了Selenium.WebDriver和Selenium.WebDriver.ChromeDriver。
我在API中创建了一个端点,该端点调用爬取项目,并运行一个方法来尝试调用new ChromeDriver()。但是它没有起作用:( 特别是,我得到了以下错误消息:
The chromedriver.exe file does not exist in the current directory or in a directory on the PATH environment variable. The driver can be downloaded at ... <url>

看起来很清楚(尽管令人失望的是它没有告诉你“当前目录”是什么意思。我将立即提交一个PR来解决这个问题)


通过观察重建期间的更改以及在线研究,我发现:
  • 所有Nuget包中的dllexe都存储在全局Nuget缓存中,而不是解决方案目录中的nuget packages文件夹。
  • chromedriver.exe似乎被复制到<solutionFolder>\<ScrapingProjectFolder>\bin\Debug\chromeDriver.exe
    • 我认为这就是ChromeDriver Nuget包所做的事情;我肯定没有自己配置它。
    • 表面上感觉这是该ChromeDriver包作为“安装此软件包以使new ChromeDriver()正常工作”的尝试而做的合理事情。
  • 深入研究WebDriver代码库,发现它正在查看的“currentDirectory”是“WebDriver.dll的位置”。
    • 在我的情况下,那是“<globalNugetPackagesCache>\selenium.webdriver\3.141.0\lib\netstandard2.0
    • 看起来我不应该试图让chromedriver.exe最终出现在这个文件夹中 - 将其复制到不同软件包的全局缓存中似乎是错误的?(大家同意吗?)
  • 这篇文章似乎得出了基本相同的结论,并说解决方案是以以下方式调用驱动程序:

    new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location))

    • 不幸的是,这条路径将带我到<solutionFolder>\<APIProjectFolder>\bin\Debug\<ScrapingProjectFolder>.dll,因为dll被复制到API项目的文件夹中。

我想到了几个解决方案,但都不是很理想:

  • 我可以将Selenium.WebDriver.ChromeDriver安装到API项目中。
    • 哎呀... API项目不知道WebDriver或Selenium,现在Scraping项目没有驱动程序exe文件了。
  • 我可以手动将exe文件复制到正确的位置。
    • 感觉不太对劲,而且很脆弱。我怀疑这会使部署变得痛苦。
  • 我可以手动将ChromeDriver构造函数指向硬编码路径,我碰巧知道该路径包含当前的exe
    • 看起来与上面类似,但不太糟糕。
  • ??? 有没有一种方法可以使项目的所有DLL等被编译到单个公共文件夹中? ???

有没有一个好的、非hacky的方法来解决这个问题。这将导致一个只需运行的git仓库,并且将来部署到服务器上也相对容易?

我描述的任何事情都错了吗,或者配置错误吗?


1
我认为一个简单的解决方案是通过VS添加项目 -> 现有项目,将chromedriver.exe添加到项目中。然后选择chromedriver.exe,将“复制到输出目录”更改为“始终复制”。 - Khanh TO
1
你有没有考虑将驱动程序的路径放入环境变量中,并在项目中读取它? - Dimitar
2个回答

5

据我了解,您有一个依赖于爬虫项目的API项目。

Scraping.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>  
    <LangVersion>7.2</LangVersion>
    <PublishChromeDriver>true</PublishChromeDriver>    
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Selenium.WebDriver" Version="3.141.0" />
    <PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="2.46.0" />
  </ItemGroup>
</Project>

API.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\Scraping\Scraping.csproj" />
  </ItemGroup>

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <LangVersion>7.2</LangVersion>
  </PropertyGroup>

</Project>

技巧是在传递项目中添加<PublishChromeDriver>true</PublishChromeDriver>,使其在运行dotnet publish API.csproj时发布chromedriver。 ChromeDriver包在NuGet包中具有自定义构建目标,因此它是自定义的

现在你可以使用

new ChromeDriver(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));

并且运行dotnet run API.csproj


我认为在这里发布Chrome驱动程序不是问题。 - LukaszBalazy
1
< PublishChromeDriver > true < /PublishChromeDriver > 对我很有帮助,将chrome.exe复制到引用程序集中。谢谢。 - Amanvir Mundra
对于下一个人:请注意,PublishChromeDriver 适用于 Selenium.WebDriver.ChromeDriver,但不适用于 Selenium.Chrome.WebDriver... - StuartLC
其他驱动程序怎么样?csproj支持IE、Edge和Firefox吗?有人试过吗? - NewBie1234

3
如果我说错了请纠正我。您有某种类库,引用了Selenium,您想要使用ChromeDriver.exe,但是出现了找不到它的错误。这很简单。 目前,您正在将类库Foo引用到API中。您的程序集位置将指向API bin位置,而chromedriver.exe位于类库bin下。 如果是这种情况,唯一需要做的就是将以下chromedriver.exe复制到最终的bin目录,即API目录。
在您的API项目中添加以下后置构建事件以复制chromedriver:
  <Target Name="PostBuild" AfterTargets="PostBuildEvent">
    <Exec Command="copy $(SolutionDir)\ClassLibrary\bin\Debug\netstandard2.0\chromedriver.exe $(TargetDir)" />
  </Target>

这将把您的chromedriver.exe复制到API bin中。稍后在初始化ChromeDriver时使用以下命令:
        var options = new ChromeOptions();
        var service = ChromeDriverService.CreateDefaultService(AppDomain.CurrentDomain.BaseDirectory);

        WebDriver = new ChromeDriver(service, options);

虽然 AppDomain.CurrentDomain.BaseDirectory 将指向您的API bin目录。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接